week8 csp模拟 C - 咕咕东的奇妙序列

 

题目

样例输入

5
1
3
20
38
56

样例输出

1
2
5
2
0

 

思路

首先,我们来观察一下数据范围:k的上限是一个1e18,暴力枚举显然过不了(甚至都算不出来)......

其中,test()函数是暴力枚举的算法,通过枚举找到当前枚举到的数字,再通过除法与取模操作来计算第k位的数字。这个函数是测试优化算法答案是否正确的,不多做说明。

优化算法:

将k为前面的数字分层,1~1到1~9为一层、1~10到1~99为一层,依次类推,每次最高位数位数加1分一层。

首先进行初始化,计算b[ i ]、c[ i ]、d[ i ]。mul[ i ]是一个乘数的数字(因为感觉每一次求幂次方太慢了,有mul数组相当于只求一次幂次方),其中:

b[ i ]是每一层最后一个数的k值,c是每一层第一个数(不是每一位的数字)最后一位的k值,d是每一层中最后一组的位数。

num()函数中:

通过b数组先对k进行分层,判断最大层数。

然后进入mid()函数,判断k位具体处在哪一组(求出1~ i 中 i 的值)。

在二分的时候,将k值减去前一层的值,求出一个相对k值。然后通过c数组求出每一组的第一个位数与最后一个位数,进行二分。最后得出的结果是k值所在组的第一个位数(返回时需要加上偏移量)。

进入ans()函数:

通过c数组求出k的大致范围u~v(其中u与v之间相差一个数量级)。然后通过除法操作求出k所在的具体数字,再通过除法操作与取模操作求出k所在位数的数字。

总之,该方法就是一个不断分层次的算法,其中一部分通过二分进行优化

代码

#include<iostream>
#include<cmath>
using namespace std;
long long a[17] = { 9,180,2700,36000,450000,5400000,63000000,720000000,8100000000,90000000000,990000000000,
10800000000000,117000000000000,1260000000000000,13500000000000000,144000000000000000,1530000000000000000 };
long long mul[10] = { 0 };
long long b[10] = { 0 };//每一层结束的个数
long long c[10] = { 0 };//每一层第一个的个数
long long d[10] = { 0 };//每一层最后一组个数

void init()
{
	mul[0] = 9;
	b[0] = 45;
	c[0] = 1;
	d[0] = 9;
	for (int i = 1; i < 10; i++)
	{
		mul[i] = mul[i-1] * 10;
		d[i] += (d[i - 1] + a[i]);
		b[i] = (i + 1)*(1 + mul[i]) * mul[i] / 2 + b[i - 1];
		for (int j = 0; j < i; j++)
			b[i] += mul[i] * a[j];
		c[i] = c[i - 1] + a[i - 1] + 1;
		cout << "d[i]=" << d[i] << endl;
	}
}



long long mid(int rankk, long long begin, long long k)
{
	long long now = k - begin + 1;
	long long left = pow(10, rankk) - 1;
	long long ll = pow(10, rankk) - 1;
	long long mid = 5 * pow(10, rankk);
	long long right = pow(10, rankk + 1) - 1;
	long long mid1 = (mid - left - 1) * (2 * c[rankk] + (rankk + 1)*(mid -ll - 2)) / 2;
	long long mid2 = (mid - left) * (2 * c[rankk] + (rankk + 1)*(mid - ll - 1)) / 2;
	long long beore = mid1;
	while (1)
	{
		if (now > mid1&&now <= mid2)
			return mid1 + begin;
		
		else if (now <= mid1)
		{
			right = mid;
			mid = (mid + left + 1) / 2;
			mid1 = (mid - ll - 1) * (2 * c[rankk] + (rankk + 1)*(mid - ll - 2)) / 2;
			mid2 = (mid - ll) * (2 * c[rankk] + (rankk + 1)*(mid - ll - 1)) / 2;
			
		}
		else
		{
			left = mid;
			mid = (right + left + 1) / 2;
			mid1 = (mid - ll - 1) * (2 * c[rankk] + (rankk + 1)*(mid - ll - 2))/2;
			mid2 = (mid - ll) * (2 * c[rankk] + (rankk + 1)*(mid - ll - 1))/2;
		}
	}

}

long long num(long long k)
{
	int i = 0;
	long long begin;
	if (k <= b[0])
		begin = 0;
	else 
	{
		for (i = 1; i < 11; i++)
		{
			if (k < b[i])
			{
				begin = b[i - 1];
				break;
			}
		}
	}
	long long j = 0;
	begin++;
	long long before = begin;
	return mid(i,begin,k);
}

long long ans(long long k, long long now)
{
	now = k - now + 1;
	int rankk = 0;
	if (now <= 9) return now;
		
	for (int i = 2; i < 10; i++)
	{
		if (now <= c[i] - i - 1)
		{
			rankk = i;
			break;
		}
			
	}

	long long x = c[rankk - 1] - rankk;
	long long remain = now -x;
	long long y = remain / rankk + mul[rankk-1] / 9;
	long long yu = remain % rankk;
	long long v = mul[rankk - yu] / 9;//取模操作
		if (yu == 0) return (y - 1) % 10;
		else return (y / v) % 10;

	
}

int test(long long k)
{
	long long c;
	long long com;
	long long pc = 0;
	for (long long i = 1; pc <= k;i++)
	{
		for (long long j = 1; j <= i; j++)
		{
			if (j < 10)
			{
				c = j;
				pc++;
				if (pc == k) return c;
			}
			else if (j < 100)
			{
				int ii = 2;
				long long v = 10;
				while (ii>0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
			else if (j < 1000)
			{
				int ii = 3;
				long long v = 100;
				while (ii > 0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
			else if (j < 10000)
			{

				int ii = 4;
				long long v = 1000;
				while (ii > 0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
			else if (j < 100000)
			{
				int ii = 5;
				long long v = 10000;
				while (ii > 0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
			else if (j < 1000000)
			{
				int ii = 6;
				long long v = 100000;
				while (ii > 0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
			else if (j < 10000000)
			{
				int ii = 7;
				long long v = 1000000;
				while (ii > 0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
			else if (j < 100000000)
			{
				int ii = 8;
				long long v = 10000000;
				while (ii > 0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
			else if (j < 1000000000)
			{
				int ii = 9;
				long long v = 100000000;
				while (ii > 0)
				{
					c = (j / v) % 10;
					v /= 10;
					pc++;
					ii--;
					if (pc == k) return c;
				}
			}
		}
	}
}

int main()
{
	int n;
	cin >> n;
	init();
	
	for (int i = 0; i < n; i++)
	{
		long long k;
		cin >> k;
		long long j = 1;
		//cout << "ans:";
		cout << ans(k, num(k)) << endl;
		//cout << "test:";
		//cout << test(k) << endl;
	}
	
	system("Pause");
}

 

总结

这个优化算法前前后后写了三天,对码力不足的我来说几个小时之内是没办法完成的。所以这个故事告诉我们,不会骗分的程序猿(程序媛)不是好SDUer...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值