csp m2 咕咕东的奇妙序列 二分

题意:

题目描述
咕咕东 正在上可怕的复变函数,但对于稳拿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组询问
接下来第i+1行表示第i个输入 ,表示询问第k项数字。

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

样例输入
5
1
3
20
38
56
样例输出
1
2
5
2
0

在这里插入图片描述


思路:

首先一定要注意审题,这道题是将序列中的数字将字符看待的即“10”不是一个个体,而是“1”和“0”,千万不能看错。

我们所需要进行的是找某项数字是什么,由于他可怕的数据范围,我们不能暴力求解,因此可以采用二分法。

我们采用二分法来判断是在哪一个组中,二分法中最初的时候左边的组L为1,右边的组R为 sqrt(2 * number) + 1 (1 + 2 + 3 + … + n = number 计算得出),然后看包括M = (L+R)/2组的前面整体序列的长度与我们要求的项数的关系,若是小于项数,则L = M +1,否则R = M。

如何得到1-k组序列总体长度,注意观察题目我们发现组与组之间长度有如下的关系:

/*
1组 to 9组 : 字符长度差值为1  level 0
10组 to 99组 : 字符长度差值为2 level 1
100组 to 999组 : 字符长度差值为3 level 2
......
*/

因此我们便可以通过计算确定是第几组,利用每一个level里的组的长度属于等差数列的性质。计算过程下述代码中有具体描述,总结来说就是首先确定在哪一个level中,然后确定是那个level中的第几个,与之前的算出来的数字相加即可得到最终的在第几组。

当我们算出在第几组之后我们便可以去求这项数字属于这组哪一个数字,即如112中的2属于第二组中的数字2。首先我们通过(项数 - 这一组之前的所有的序列的长度)则到这一项属于这一组中的第几个,随后我们同样采取上面的level思想 ,1到9 每个元素一个字符长度,10到99两个字符长度,这样算下去就可以知道这一项在哪一个层的第多少个,然后再取模level,若是结果为0,则正好是一个数的最后一个,我们将他输出;若不是则出现了他在某一个数的不是最后的位置,我们要特殊处理。


总结:

审题很重要!
数学很重要!
二分很重要!

对于这种数据范围很大的题目,找元素时可以利用二分的思想;
可以利用数学计算简化操作,如等差数列的计算方法;


代码:

#include<stdio.h>
#include<algorithm>
#include<string>
using namespace std;

long long getlength(long long number)
{
	long long ans = 0;
	while (number > 0)
	{
		ans++;
		number = number / 10;
	}
	return ans;
}

/*
1组 to 9组 : 字符长度差值为1  level 0
10组 to 99组 : 字符长度差值为2 level 1
100组 to 999组 : 字符长度差值为3 level 2
......
*/

long long getSum(long long group)//得到前group组数据的长度和
{
	if (group == 0)return 0;
	long long length = getlength(group);
	long long sum = 0;
	long long level = 0;  //上述level
	long long power = 1;  //权重
	long long before_end = 0; //上一层最后一个
	for (long long i = 1; i < length; i++)
	{
		long long part_length = (long long)9 * power; //每一level里的数的个数
		long long sum_first =before_end + level + 1;
		long long sum_end = before_end + part_length * (level + 1);
		before_end = sum_end;
		sum += (sum_first + sum_end) * part_length / 2;  //(首项+尾项)* 项数 / 2
		level++;
		power = power * 10;
	}

	long long thisLevel_length = group - power + 1;
	long long sum_first = before_end + level + 1;
	long long sum_end = before_end + thisLevel_length * (level + 1);
	before_end = sum_end;
	sum += (sum_first + sum_end) * thisLevel_length / 2;  //(首项+尾项)* 项数 / 2

	return sum;
}

long long getGroup(long long number)
{
	long long l = 1;
	long long r = sqrt(2 * number) + 1;

	while (l < r)
	{
		long long m = (l + r) / 2;
		if (getSum(m) < number)l = m + 1;//代表number一定不在前m层
		else
			r = m;
	}
	return l;
}


char getvalue(long long group, long long number)
{
	long long this_partLength = number - getSum(group - 1);
	//现在确认是这一组里面的哪个数
	long long power = 1;
	long long level = 1;
	long long last = this_partLength;
	long long num = 0;
	while (last > 9 * power * level)
	{
		last -= 9 * power * level;
		num += 9 * power;
		power = power * 10;
		level++;
	}

	long long lo = last % level;
	long long tem = last / level;
	if (lo != 0)
	{
		tem++;
		num = num + tem;//num是这个位置属于的数
		return to_string(num)[lo - 1];//这个数位置上的字符
	}
	else
	{
		num = num + tem;//num是这个位置属于的数
		return to_string(num)[level - 1];//这个数位置上的字符
	}
}



int main()
{
	int q;
	scanf("%d", &q);
	for (int i = 0; i < q; i++)
	{
		long long ques;
		scanf("%lld", &ques);

		long long group = getGroup(ques);//求得在第几组
		char ans = getvalue(group, ques); //求得结果
		printf("%c\n", ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值