codeforces--E1. Numerical Sequence (easy version)--前缀和+双重二分

E1. Numerical Sequence (easy version)
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
The only difference between the easy and the hard versions is the maximum value of k.

You are given an infinite sequence of form “112123123412345…” which consist of blocks of all consecutive positive integers written one after another. The first block consists of all numbers from 1 to 1, the second one — from 1 to 2, the third one — from 1 to 3, …, the i-th block consists of all numbers from 1 to i.

So the first 56 elements of the sequence are “11212312341234512345612345671234567812345678912345678910”. Elements of the sequence are numbered from one. For example, the 1-st element of the sequence is 1, the 3-rd element of the sequence is 2, the 20-th element of the sequence is 5, the 38-th element is 2, the 56-th element of the sequence is 0.

Your task is to answer q independent queries. In the i-th query you are given one integer ki. Calculate the digit at the position ki of the sequence.

Input
The first line of the input contains one integer q (1≤q≤500) — the number of queries.

The i-th of the following q lines contains one integer ki (1≤ki≤109) — the description of the corresponding query.

Output
Print q lines. In the i-th line print one digit xi (0≤xi≤9) — the answer to the query i, i.e. xi should be equal to the element at the position ki of the sequence.

Examples
inputCopy

5
1
3
20
38
56

outputCopy

1
2
5
2
0

inputCopy

4
2132
506
999999999
1000000000

outputCopy

8
2
9
8

Note
Answers on queries from the first example are described in the problem statement.

题意:一个字符串呈现这种规律产生,112123123412345123456
进行q次查询,问第k个字符是什么。

题解:首先观察字符串规律可知道,1,12,123,1234,依次进行拼接,就相当于用i从1开始跑循环,那么就是
i=1:s[1]=1
i=2:s[2]=s[1]+12
i=3:s[3]=s[2]+123
i=4:s[4]=s[4]+1234
我们可以通过这种规律得到前缀和,可以知道,如果我们可以确定第k个字符的区间是在哪个数字结尾的区间里,就可以很快缩小查询范围:
前缀和:
a[1]= 1
a[2]= 3
a[3]= 6
a[4]= 10
a[5]= 15
a[6]= 21
a[7]= 28

分两种情况:
(1)举例如果k=20:
可以知道a[5]=15 (2)举例如果k=15:
可以知道a[5]=15=k;
说明第k个字符就是以数字5结尾的子字符串中的最后一个,直接5%10得到5

可以知道前缀和都是增长的,所以这里可以直接二分查找得到对应的区间段,这里是第一次二分。

然后是第二次二分,我们已经知道可以很快得到对应区间段的方法了,现在怎么具体确定是哪一个呢
a[7]= 28
a[8]= 36
a[9]= 45
a[10]= 56
a[11]= 69
a[12]= 84
a[13]= 101
a[14]= 120
a[15]= 141

比如k=115
通过刚才说的可以知道在以14结尾的子字符串内,然后k-=a[13],k=14,相当于现在查询以14结尾形成的字符串内的第14个字符是啥:
14:1234567891011121314
那么是否可以再用一次前缀和,比如
b[1]= 1
b[2]= 2
b[3]= 3
b[4]= 4
b[5]= 5
b[6]= 6
b[7]= 7
b[8]= 8
b[9]= 9
b[10]= 11
b[11]= 13
b[12]= 15
b[13]= 17
b[14]= 19
b[15]= 21

b[x]表示:1+2+3+4+…+x+x+1过程中,到数字x时已经有的数字长度,那么同理的,再利用第一次二分时的思想,再二分直接找到是第几个数字结尾时,那么就针对这个数字求解,比如刚才这个例子,我们刚才经过第一次运算已经知道了k=14,那么在b中,b[11]

AC代码

#include
using namespace std;
typedef long long ll;
ll a[1000000],b[1000000];
void init()
{
	ll ans=0;
	ll sum=0;
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	for(int i=1;i<=1000000;i++)
	{
		if(i>=1&&i<=9)
		ans+=1;
		else if(i>=10&&i<=99)
		ans+=2;
		else if(i>=100&&i<=999)
		ans+=3;
		else if(i>=1000&&i<=9999)
		ans+=4;
		else if(i>=10000&&i<=99999)
		ans+=5;
		
		sum+=ans;
		a[i]=sum;
	//	cout<1e9)
		{
	//		cout<=1&&i<=9)
		ans+=1;
		else if(i>=10&&i<=99)
		ans+=2;
		else if(i>=100&&i<=999)
		ans+=3;
		else if(i>=1000&&i<=9999)
		ans+=4;
		else if(i>=10000&&i<=99999)
		ans+=5;
		b[i]=ans;
	//	cout<>q;
	while(q--)
	{
		int k;
		scanf("%d",&k);
		int l=1,r=21836,fin;
		while(l<=r)
		{
			int mid=(l+r)/2;
			if(a[mid]<=k)
			{
				l=mid+1;
				fin=mid;
			}
			else r=mid-1;
		}
		k-=a[fin];
		if(k==0)
		{
			printf("%d\n",fin%10);
		}
		else
		{
			int l=1,r=21836,fin;
			while(l<=r)
			{
				int mid=(l+r)/2;
				if(b[mid]<=k)
				{
					l=mid+1;
					fin=mid;
				}
				else r=mid-1;
			}
			k-=b[fin];
			if(k==0)
				printf("%d\n",fin%10);
			else
			{
				fin+=1;
				vectorp;
				p.clear();
				while(fin>0)
				{
					p.push_back(fin%10);
					fin/=10;	
				}	
				printf("%d\n",p[p.size()-k]);
			}
		}
	} 
	return 0; 
}

你可能感兴趣的:(二分查找,前缀和)