咕咕东的奇妙序列【二分】【数学规律】【等差数列】

题目描述

咕咕东 正在上可怕的复变函数,但对于稳拿A Plus的 咕咕东 来说,她早已不再听课,此时她在睡梦中
突然想到了一个奇怪的无限序列:112123123412345 …这个序列由连续正整数组成的若干部分构成,其
中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所
有数字,第i部分总是包含1至i之间的所有数字。所以,这个序列的前56项会是
11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是
5,第38项是2,第56项是0。咕咕东 现在想知道第 k 项数字是多少!但是她睡醒之后发现老师讲的东西
已经听不懂了,因此她把这个任务交给了你。

输入格式

输入有多行组成。
第一行一个整数 q 表示有 q 组询问(1 <= q <= 500)
接下来第 i+1 行表示第 i 个输入 ki,表示询问第 ki 项数字。(1 <= ki <= 10^18)

输出格式

输出包含 q 行。
第 i 行输出对询问 ki 的输出结果。

样例输入

5
1
3
20
38
56

样例输出

1
2
5
2
0
数据点

问题分析

因为 k 是特别大的,不能采取暴力的枚举每一位,这样子很明显会超时。我们看一下序列的规律,容易发现:我们可以先进行分组,第i组即为题干中的第i部分总是包含1至i之间的所有数字。1-9组每个组的项数是等差增加的,d=1;10到99组每个组的项数是等差增加的,d=2;…等差数列求和有公式,利用公式可以求得项数和。利用二分法,求出k所在的组数,再在相应的组数里求出项值。每一组的项一样是等差数列,找到规律求解。
注意要采用数据类型long long,一些乘法也要注意,否则可能会wrong answer。

#include<stdio.h>
long long solve(long long n){
	long long sum=0,a=1,s=9,d=1;
	while(n){
		if(n>s){
			sum+=a*s+s*(s-1)*d/2;
			n-=s;
		}
		else{
			sum+=a*n+n*(n-1)*d/2;
			return sum;
		}
		a+=s*d+1;s*=10;d++;
	}
}
int main(){
	long long k;
	int q;
	scanf("%d",&q);

	for(int i=0;i<q;i++){
		scanf("%lld",&k);
		long long l=1,r=1e9,ans=0;
		while(l<=r){
			long long mid=(l+r)>>1;
			long long t=solve(mid);
			if(t<=k){
				ans=mid;
				l=mid+1;
			}
			else r=mid-1;
		} 
		k=k-solve(ans);
		
		long long n=9,ans1=0,d=1;
		while(k){
			if(k>n*d){
				ans1+=n;
				k-=n*d;
			}
			else{
				ans1+=k/d;
				k=k%d;
				break;
			}
			d++;n*=10;
		}
		if(ans1==0)ans1=ans;
		if(k==0) printf("%lld\n",ans1%10);
		else{
			ans1+=1;
			while(d!=k){
				d--;
				ans1/=10;
			}
			printf("%lld\n",ans1%10);
		} 
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值