cspM3-3

题目要求

咕咕东 正在上可怕的复变函数,但对于稳拿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个输入 ,表示询问第 ki项数字。

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

样例输入
-1
5 1 3 20 38 56

样例输出
1 2 5 2 0
cspM3-3_第1张图片

求解思路
  • 将1……i看为一组,因为这里k的上限达到了10^18,预估序列达到1~1E9,无法存储全部组的信息,数组会超出限制大小。
  • 计算方式为:首先二分答案找到ki所在的组数,从1~1E9中二分寻找,计算包括1……num前的num组一共有x位,若ki>x,则所在组数大于num,否则,所在组数小于num,找到ki所在的组数后,ki减去包括1……num-1的前num-1组的位数,即为ki在第num组的第几位,进而计算该位为多少。
  • 计算第几组和某个组的第几位的方式均为判断范围,然后通过等差数列求和公式计算,计算过程比较暴力,为了降低复杂度,将一些固定的中间变量提前算出设定。
  • 该题考试很必要分组骗分,因为10^6以内可以预存每组信息,这样计算过程会简单很多。
代码
#include
#include
#define range (long long)1E9
using namespace std;
typedef long long ll;
ll cocu(ll num)//计算包括1……num前一共有多少位
{
	if (num <= 9)
	{
		ll sum = 0;
		sum = (num + 1) * num / 2;
	
		return sum;
	}
	if (num <= 99)
	{
		ll sum = 0;
		ll front = 0;
	
		front = 9; sum = 45;

		sum = sum + (num - 9) * front + (1 + num - 9)*(num - 9);
		return sum;
	}
	if (num <= 999)
	{
		ll sum = 0;
		ll front = 0;

		front = 189; sum = 9045;
	
		sum = sum + (num - 99) * front + (1 + num - 99) * (num - 99) * 3 / 2;
		return sum;
	}
	if (num <= 9999)
	{
		ll sum = 0;
		ll front = 0;
	
		front = 2889; sum = 1395495;
		
		
	
		sum = sum + (num - 999) * front + (1 + num - 999) * (num - 999) * 4 / 2;
		return sum;
	}
	if (num <= 99999)
	{
		ll sum = 0;
		ll front = 0;
	
		front = 38889; sum = 189414495;
		
		sum = sum + (num - 9999) * front + (1 + num - 9999) * (num - 9999) * 5 / 2;
		return sum;
	}
	if (num <= 999999)
	{
		ll sum = 0;
		ll front = 0;

		front = 488889; sum = 23939649495;
		
		sum = sum + (num - 99999) * front + (1 + num - 99999) * (num - 99999) * 6 / 2;
		return sum;
	}
	if (num <= 9999999)
	{
		ll sum = 0;
		ll front = 0;
	
		front = 5888889;
		sum = 2893942449495;
	
		sum = sum + (num - 999999) * front + (1 + num - 999999) * (num - 999999) * 7 / 2;
		return sum;
	}
	if (num <= 99999999)
	{
		ll sum = 0;
		ll front = 0;
	
		front = 68888889;
		sum = 339393974949495;
		
		sum = sum + (num - 9999999) * front + (1 + num - 9999999) * (num - 9999999) * 8 / 2;
		return sum;
	}
	if (num <= (ll)999999999)
	{
		ll sum = 0;
		ll front = 0;
	
		front = 788888889;
		sum = 38939394344949495;
		
		sum = sum + front * (num - 99999999)  + ((ll)(1 + num - 99999999)) * (num - 99999999) * 9 / 2;

		return sum;
	}

}
ll findans(ll x)//二分答案
{
	ll l = 1, r = range, mid = 0, ans = 0;
	while (l <= r)
	{
		mid = (l + r) >> 1;
		//cout << "ddd" <
		if (cocu(mid) >= x)
		{
			ans = mid;
			r = mid - 1;
		//	cout << "dangqianzushu" << ans << endl;
		}
		else
			l = mid + 1;
	}
	return ans;
}

int weishu = 0;//从右往左数第几位
ll findout(ll t)//返回当前位数所在数
{
	if (t <= 9)
	{
		weishu = 1;
		return t;
	}
	if (t <= 189)
	{
		ll sum = 9;
		ll d = (t - sum - 1) / 2 + 1 + 9;
		weishu = 2- (t - sum -1) % 2 ;
		
		return d;
	}
	if (t <= 2889)
	{
		//cout << "aaa";
		ll sum = 189;
		ll d = (t - sum - 1) / 3 + 1 + 99;
		weishu = 3 - (t - sum - 1) % 3;
	
		return d;

	}
	if (t <= 38889)
	{
		ll sum = 2889;
		ll d = (t - sum - 1) / 4 + 1 + 999;
		weishu = 4 - (t - sum - 1) % 4;
		return d;

	
	}
	if (t <=488889)
	{
		ll sum = 38889;
		ll d = (t - sum - 1) / 5 + 1 + 9999;
		weishu =5 - (t - sum - 1) % 5;
		return d;

	}
	if (t <= 5888889)
	{
		ll sum = 488889;
		ll d = (t - sum - 1) / 6 + 1 + 99999;
		weishu = 6 - (t - sum - 1) % 6;
		return d;

	}
	if (t <= 68888889)
	{
		ll sum = 5888889;
		ll d = (t - sum - 1) / 7 + 1 + 999999;
		weishu = 7 - (t - sum - 1) % 7;
		return d;

	}
	if (t <= (ll)788888889)
	{
		ll sum = 68888889;
		ll d = (t - sum - 1) / 8 + 1 + 9999999;
		weishu = 8 - (t - sum - 1) % 8;
		return d;

	}
	
		ll sum = 788888889;
		ll d = (t - sum - 1) / 9 + 1 + 99999999;
		weishu = 9 - (t - sum - 1) % 9;
		return d;

	//}

}

ll temp;

int main()
{
	//cout << cocu(100000) << endl;       //189453384
	int q;
	scanf("%d", &q);
	for (int i = 0; i < q; i++)
	{
		scanf("%lld",&temp);
		ll zushu = findans(temp);
		//cout <<"zushu"<< zushu<
		temp = temp - cocu(zushu - 1);//该组第几个
	//	cout <<"dijige"<< temp << endl;
		ll con= findout(temp);
		ll po = 1;
		for (int j = 0; j < weishu; j++)
		{
			po = po * 10;
		}
		printf("%lld\n", (con % po) / (po / 10)); 
		
	}
}

你可能感兴趣的:(算法)