Codeforces Round #587 (Div. 3) E2. Numerical Sequence (hard version)(双重二分)

题目链接
Codeforces Round #587 (Div. 3) E2. Numerical Sequence (hard version)(双重二分)_第1张图片
Codeforces Round #587 (Div. 3) E2. Numerical Sequence (hard version)(双重二分)_第2张图片
思路:这道题真的搞疯我了,各种细节各种bug,AC得很不容易,感觉还是码力不够得关系QAQ
由于写得时候各种调式找问题,代码已经奇丑无比了,还是讲讲思路吧。。。。
由于这里的k很大,我们已经无法向easy版本那么把b数组打表出来解决了,那么就换种思路,首先还是往当给出k的时候,判断k会落在那个数字这个方面想,我们可以一步步缩小范围,首先我们是可以确定k是在1-9、10-99、100-999、1000-9999.。。。这里的其中一个了,假设它确定在1000-9999,那么我们就要确定它在1000-9999里的那个数字,这个怎么求呢?最后一个数字是1000的时候的长度其实是O(1)用公式就可以求得,我们发现最后一个数字是1000、1001、1002.。。。得时候他们得长度其实是个等差数列,所以可以通过二分加等差数列求和公式确定k在那个数字上,假设已经确定了k在1001上,那么现在要进一步确定k是在1-1001里得哪一个了,这里就又要二分判断,假设二分得到k在数字12,那么最终得答案肯定不是1就是2,判断一下x还剩几个就好了。

#include 
using namespace std;
const int maxn=2e5+1;
typedef long long ll;
ll x,k,b[maxn],a[maxn];
vector<int>temp1;
int main()
{
	ll t1,temp=1;
	a[0]=0;
	for(ll i=1;;++i)
	{
		t1=temp*9;
		b[i]=b[i-1]+t1*(a[i-1]+i)+t1*(t1-1)*i/2;
		temp*=10;
		a[i]=a[i-1]+t1*i;
		if(b[i]>=1e18) break;
	}
	temp=1;
	for(int i=0;i<100;++i) a[i]=0;
	for(ll i=1;;++i)
	{
		t1=temp*9;
		temp*=10;
		a[i]=a[i-1]+t1*i;
		if(a[i]>=1e18) break;
	}
	int q,k;
	scanf("%d",&q);
	while(q--)
	{
		temp=1;
		temp1.clear();
		scanf("%lld",&x);
		for(ll i=1;;++i)
		if(x<=b[i]) {k=i;break;}
		for(int i=1;i<=k-1;++i) temp*=10;
		ll l=temp,r=temp*10-1,first=a[k-1]+k;
		x-=b[k-1];
		while(l<=r)//二分查找在那一段位数 
		{
			ll mid=(l+r)>>1,n=mid-temp+1;
			if(n*first+n*(n-1)*k/2<=x) l=mid+1;
			else r=mid-1;
		}
		ll n=(l-temp);
		x-=n*first+n*(n-1)*k/2;
		//cout<
		if(x==0) {
			printf("%d\n",(l-1)%10);continue;
		}
		ll L=1,R=l;//找到1到l中的第x个数在哪儿 
		while(L<=R)
		{
			ll mid=(L+R)>>1;
			ll ttt=mid,cnt=0;
			while(ttt>0) ttt/=10,cnt++;
			if((mid-pow(10,cnt-1)+1)*cnt+a[cnt-1]<x) L=mid+1;
			else R=mid-1;
		}
		ll cnt=0;
		ll s=L;
		while(s>0) s/=10,cnt++;
		x-=(L-pow(10,cnt-1))*cnt+a[cnt-1];
		//cout<
				while(L>0) temp1.push_back(L%10),L/=10;
					printf("%d\n",temp1[(temp1.size()-x)]);
	}
}

你可能感兴趣的:(二分)