C-咕咕东的奇妙序列
题目描述
题目思路
这道题在我刚做的时候是没有一点思路的,之后想想并请教了别人之后才知道是怎样的思路。通过计算我们可以知道,数据为1e8的时候,此时最大数的位数不超过9位,这样我们知道了数据边界就可以使用二分的方法来求解,因为这个题目的数据量非常大,如果就是简单的遍历绝对是会TLE的(不用想)。然后通过找规律发现:
1~9每多一个数就多一位;
10~99每多一个数就多两位;
100~999每多一个数就多三位;
…
以此类推我们可以得到一组等差数列,这样我们求数字的长度就可以使用等差数列的求和公式来做。
总体思路是先通过二分查找的方法查找到k在几位数的部分,即确定ans,并更新k,然后在这个部分里面在使用二分的方法找到那个数字ans,然后再在这个ans里面找到第k个位置的数即是答案。
代码实现
#include <iostream>
#include <algorithm>
#include <sstream>
using namespace std;
#define ll long long
#define _for(i,a,b) for(int i = (a); i < (b); i++)
#define _rep(i,a,b) for(int i = (a); i <= (b); i++)
ll q,k,sum1,sum2,l,r,mid,ans;
void get_sum(long long x)
{
ll max = 1,n = 0,d = 0;
sum1 = 0,sum2 = 0;
while(1)
{
max *= 10;
d++;
if(x >= max)
{
n = max - max/10;
sum1 += (sum2+d)*n + n*(n-1)*d/2;
sum2 += n*d;
}
else
{
n = x - max/10 + 1;
sum1 += (sum2+d)*n + n*(n-1)*d/2;
sum2 += n*d;
break;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> q;
while(q--)
{
cin >> k;
l = 0,r = 1e9;
while(l <= r)
{
mid = (l + r) >> 1;
get_sum(mid);
if(sum1 < k)
{
ans = mid;
l = mid + 1;
}
else{
r = mid - 1;
}
}
get_sum(ans);
k -= sum1;
l = 0,r = ans + 1;
while(l <= r)
{
mid = (l + r) >> 1;
get_sum(mid);
if(sum2 < k)
{
ans = mid;
l = mid + 1;
}
else{
r = mid - 1;
}
}
get_sum(ans);
k -= sum2;
ans++;
string s;
stringstream ss;
ss << ans;
ss >> s;
cout << s[k-1] - '0' << endl;
}
}