codeforces 1216E2 Numerical Sequence (hard version)

本文详细解析了Codeforces比赛中的E2题目,介绍了如何通过前缀和、二分查找及定位技巧来解决复杂的数列问题,提供了一种高效算法实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://codeforces.com/contest/1216/problem/E2

这种找位置的题我还挺不熟练的,不过今天一发就过了,第一次ak div3,开心

先考虑1  12  123  1234这种段,我们先确定在哪一段里面,

我们可以发现,这些段的长度是1,2,3.....9,11,13,....这样的,也就是最后一位变成2位数后,每多一个数+2,3位数就每多一个数+3以此类推。

这样,我们记录一下sum,求出末尾位数为i的段的总长度,然后做前缀和,我们就可以定位到k在末位数字位数的哪一段里,然后k减去前面的sum前缀和。

接着,由于当前末位数字为id位,所以每一段的长度是等差数列,可以用二分定位到是哪一段,也就是末位数字的具体数值是多少,并k减去之前的

接下来我们再考虑在这一段123......len中,第k个在哪里,那么前9个是1,后面是一段2,再后面是一段3。。。和之前一样,定位到到底在哪个数字中,再定位到这个数字中的第几位数就行了

#include<bits/stdc++.h>
#define maxl 300010
using namespace std;

int n,m,ans,up,top;
unsigned long long k;
unsigned long long st[201],ed[201],sum[201],num[201];
int s[maxl];

inline void init()
{
	unsigned long long mi=1;
	st[0]=0;ed[0]=0;
	for(int i=1;i<=64;i++)
	{
		st[i]=ed[i-1]+i;
		num[i]=((mi*10-1)-mi+1);
		ed[i]=st[i]+i*(num[i]-1);
		sum[i]=sum[i-1]+(st[i]+ed[i])*num[i]/2;
		up=i;
		if(sum[i]>=1e18)
			break;
		mi*=10;
	}
}

inline void prework()
{
	scanf("%lld",&k);
}

inline int findans(unsigned long long x,int pos)
{
	top=0;
	while(x>0)
	{
		s[++top]=x%10;
		x/=10;
	}
	return s[top-pos+1];
}

inline void mainwork()
{
	unsigned long long mi=1,id;
	for(int i=1;i<=up;i++)
	{
		if(sum[i]>=k)
		{
			id=i;
			break;
		}
		mi*=10;
	}
	k-=sum[id-1];
	unsigned long long l=0,r=num[id]-1,mid;
	unsigned long long t,d;
	while(l+1<r)
	{
		mid=(l+r)>>1;
		t=(st[id]+id*(mid-1)+st[id])*mid/2;
		if(t<k)
			l=mid;
		else
			r=mid;
	}
	if((st[id]+id*(r-1)+st[id])*r/2<k)
		d=r;
	else
		d=r-1;
	k-=(st[id]+id*(d-1)+st[id])*d/2;
	unsigned long long len=st[id]+d,sum=0;
	for(int i=1;i<=up;i++)
	{
		if(k<=num[i]*i)
		{
			id=i;
			break;
		}
		k-=num[i]*i;sum+=num[i];
	}
	unsigned long long val=k/id;
	if(k%id==0)
		ans=findans(val+sum,id);
	else
		ans=findans(val+sum+1,k%id);
}

inline void print()
{
	printf("%d\n",ans);
}

int main()
{
	init();
	int t=1;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值