【Week8实验 C】咕咕东的奇妙序列【二分】

题意:

一个奇怪的无限序列:112123123412345…这个序列由连续正整数组成的若干部分构成,其中第一部分包含1至1之间的所有数字,第i部分包含1至i之间的所有数字。注意12345678910的10为两项。现在要求出第k项数字是多少。


思路:

先看每一部分的长度:1 2 3 4 5 6 7 8 9 11 13 15…能看出这由多个等差数列组成,我们就可以用等差数列前n项和公式求出某一部分在序列中的位置。利用二分可以得出第ans部分里有第k项数字。然后看第ans部分:1 2 3 4 5 6 7 8 9 11 13…(这是每个数对应的位置,如10对应位置11),这也是等差数列,且与第一部分的数列相同。我们就可以求出第ans个数字里有第k项数字,且能求出数字在ans中的位置,这样就得到了答案。
func函数:当flag为1时,计算某一部分在序列中的位置,即在总序列中112123…x共多长。用等差数列求和公式来算,求项数公差首项末项的公式可以看代码。最终得到的sum就是某一部分在序列中的位置。当flag为2时,应计算第ans部分123…ans中的数字x的位置,即在123…ans中123…x有多长。可以发现这次要求的是数列的最后一项an。
然后是二分:一开始求第k项在第几部分里,l=0,r=1e9,逐渐逼近,求出ans。k=k-func(ans-1,1)后,答案就是第ans部分123…ans中的第k位,继续用二分算,l=0,r=ans+1,逐渐逼近,求出ans。此时答案就在数字ans中,且为第k位,k=k-func(ans-1,2)。最后输出ans的第k位即可。


总结:

一道二分题目,对应CSP T4。题目很难,做题时只暴力输出了前56项的数据,拿到30分。补题时也写了很久。不用二分一点点求第k项会T,用pow(10,ws)函数会WA,应该是返回值double转换为long long int出现错误了。这道题能想到二分也很不容易。


代码:

#include <iostream>
#include <cmath>
#include <string>
using namespace std;
//用pow(10,ws)为WA,应该是返回值double转换为long long int时出现错误 
long long int k;
int z[57]= {0,1,1,2,1,2,3,1,2,3,4,1,2,3,4,5,1,2,3,4,5,6,1,2,3,4,5,6,7,
            1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,0};

//flag=1时,在总序列中返回112123......x共多长
//flag=2时,在一个数字生成的序列123...ans中返回123..x共多长 
long long int func(long long int x,int flag)
{
	long long int sum=0,ws=0;	//记录数字当前位数,如10表示为两位数10~99 
	//等差数列求前n项和,其中每一项都是由一个数字生成的序列长度 
	long long int n=0,a1=0,an=0,d=0; 
	while(1)
	{
		if(ws==0)	ws=1;
		else 	ws=ws*10;
		if(x>ws*10-1)
		{	
			n=10*ws-ws;				//项数
			d++;					//公差
			a1=an+d;				//首项,为上一次的末项再加上这次的公差 
			an=a1+(n-1)*d;			//末项 
			sum=sum+(a1+an)*n/2;
		}
		else	//x与ws同一位数 
		{
			n=x-ws+1;
			d++;
			a1=an+d;
			an=a1+(n-1)*d;
			sum=sum+(a1+an)*n/2;
			break;
		}
	}
	if(flag==1)	return sum;
	else 	return an; 
}
int main()
{
	int q;
	cin>>q;
	for(int zs=0; zs<q; zs++)
	{
		cin>>k;
		if(k<=56)
			cout<<z[k]<<endl;
		else
		{
			long long int l=0,r=1e9,mid,ans=0;
			while(l<=r)
			{
				mid=(l+r)/2;
				if(func(mid,1)>=k)
					ans=mid,r=mid-1;
				else	l=mid+1;
			}
			//此时答案在112123.....123...ans中
			k=k-func(ans-1,1);	//答案为123...ans中的第k位
			l=0,r=ans+1;
			while(l<=r)
			{
				mid=(l+r)/2;
				if(func(mid,2)>=k)
					ans=mid,r=mid-1;
				else	l=mid+1;
			}
			//此时答案在数字ans中
			k=k-func(ans-1,2);	//答案锁定为数字ans中的第k位
			string theAns=to_string(ans);
			cout<<theAns[k-1]<<endl; 
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值