蓝桥杯: 历届试题 数字游戏(模拟超时,靠数学推导公式)

历届试题 数字游戏
时间限制:1.0s 内存限制:256.0MB

问题描述
  栋栋正在和同学们玩一个数字游戏。

游戏的规则是这样的:栋栋和同学们一共n个人围坐在一圈。栋栋首先说出数字1。接下来,坐在栋栋左手边的同学要说下一个数字2。再下面的一个同学要从上一个同学说的数字往下数两个数说出来,也就是说4。下一个同学要往下数三个数,说7。依次类推。

为了使数字不至于太大,栋栋和同学们约定,当在心中数到 k-1 时,下一个数字从0开始数。例如,当k=13时,栋栋和同学们报出的前几个数依次为:
  1, 2, 4, 7, 11, 3, 9, 3, 11, 7。

游戏进行了一会儿,栋栋想知道,到目前为止,他所有说出的数字的总和是多少。
输入格式
  输入的第一行包含三个整数 n,k,T,其中 n 和 k 的意义如上面所述,T 表示到目前为止栋栋一共说出的数字个数。
输出格式
  输出一行,包含一个整数,表示栋栋说出所有数的和。
样例输入
3 13 3
样例输出
17
样例说明
  栋栋说出的数依次为1, 7, 9,和为17。
数据规模和约定
  1 < n,k,T < 1,000,000;
  
【思路】
模拟做可以水走66分的分数,思路比较直观,但时间复杂度到了O(n * T),这题数据量如此之大,要过后面两个数据点就不可能了

模拟代码:

/*
相当于说的数字对k取模
数据量偏大,最后的sum要用longlong保证不溢出
模拟超时。。66分 
*/
#include<iostream>
using namespace std;
#define ll long long

int n, k, T;

int main()
{
	//用scanf提速,还是不行 
	scanf("%d%d%d", &n, &k, &T);
	int dis = 1;				//表示每次报的数与前一个数的间隔dis。初始为1
	int num = 1;				//报的数字,初始值也是为1
	int cnt = 0;				//记录冻冻报出的数个数
	ll sum = 0;					//记录冻冻报数的总和 
	int i = 1;
	while(cnt < T)
	{
		cnt++;
		sum += num;
		for(i = 1;i <= n;i++){num = (num + dis++) % k;}
	}
	cout << sum << endl;
	return 0;
}

那么怎么才能通过呢?
网上查阅相关资料后,发现唯一一条路就是数学推导公式了。。orz
是这样的,我们的结果其实只依赖于冻冻所报的数字,因此,我们如果能用某种数学公式直接将冻冻每次报的数算出来,那么我们就能在O(T)的时间复杂度里面解决问题了!
推导发现如下规律:

比如第一个测试样例:

冻冻第一个数:1
冻冻第二个数:(1 + (1 + 2 + 3)) % k = 7
冻冻第三个数:(7 + (4 + 5 + 6)) % k = 9
...
这个道理细细一想也能明白,间隔dis每次都在自增1
所以公式应该是
Xk = (Xk-1 + (dis * n + SUM(1, n - 1))) % K;

公式推出来了,代码就没问题了吗?
注意另一个坑点:本题数据量很大,所有参与计算的变量都要用long long,否则溢出到你怀疑人生
AC代码:

/*
本题模拟就算做到极致,也有时间复杂度O(n * T),AC不了
只能通过数学推导,每次直接算出冻冻的数才行 
*/
#include<iostream>
using namespace std;
#define ll long long

const int maxn = 1000005;
ll n, k, T;
ll s[maxn];

void init()					//预处理和数组
{
	ll i;
	s[1] = 1;
	for(i = 2;i <= maxn;i++)
		s[i] = s[i - 1] + i;
}

int main()
{
	cin >> n >> k >> T;
	ll dis = 1;				//记录每次所报数字与上一个数字的间隔
	ll num = 1;
	ll sum = 1;
	ll cnt = 1;
	init();
	while(cnt < T)
	{
		cnt++;										//第二次开始 
		num = (num + dis * n + s[n - 1]) % k;		//冻冻第二个报的数字
		sum += num;
		dis += n; 
	} 
	cout << sum << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值