【蓝桥杯】 历届试题 数字游戏(数列)—— 酱懵静

历届试题 数字游戏

问题描述
栋栋正在和同学们玩一个数字游戏。
游戏的规则是这样的:栋栋和同学们一共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;



---分割线---



分析:
我们看一下报数时会出现的数字(不做取余处理,上面是报数,下面是差值):

1  	2  	4  	7  	11	 16	  22    29	  37	46	……			
  1	  2	  3	  4	   5    6	  7	    8	 9	 ……

我们单独看一下栋栋的报数: 1 7 22 46……
不难发现这些数据服从以下规律:

 1 
 1 + ( 1 + 2 + 3 ) = 7
 1 + ( 1 + 2 + 3 ) + ( 4 +5 + 6 )  =  22

由此可以得到栋栋报数的一个通项公式:num = 1 + n (n+1) / 2 (其中n表示栋栋报数时对应的序号-1,显然n取值是:0,3,6,9,……)

知道了通项公式,以及总的循环次数,直接循环求和就是
但是,这样的下场是:拿不到满分
为什么?
仔细看题,T和k的取值最大会接近100 0000,这时候如果仅仅只是利用上面的这个公式来求每次的报数num并求和然后再取余,则num总会在某个值时发生溢出现象(num的数量级极限会取到100 0000^4,显然溢出了long long),这时后面所有的num都将错误。所以这个方案是不可行的

为了避免这个溢出问题,我们应该想到利用前一次num的数值与下一次num的数值之间的差值来完成对num的变化,即利用这个差值来实现数据的更新(因为这之间的差值不会超过long long)
于是我们需要来推算一下(或者说来求出)这个差值量与序数n的关系,即求出这个函数。我们假设,有N个同学参与数字游戏,那么对于一个周期(N)而言
栋栋两次报数的差值是一个关于n的函数:
△x=1+(n+N) [(n+N)+1] / 2 - [ 1+n(n+1) / 2 ] = N(N+2*n+1)/2
这样就可以利用上面的公式,通过上一次num的值,用加法来把下一次的num计算出来,然后取余,叠加到sum中(这样显然是不会产生数据溢出的现象的)

下面给出本题的完整代码:


---分割线---


#include<iostream>
using namespace std;
int main()
{
	int n,k,T;
	cin>>n>>k>>T;
	long long sum=0,i=0,x=1;//sum是最后的总和,i是东东报数时的序号(从0开始),x是第i-1次东东报数后取余再加上第i-1到第i次之间的差值的值 (即第i次的值)
	while(T--)
	{ 
		sum+=x;
		x+=(n+2*i+1)*n/2;  
		x%=k;
		i+=n;
	}
	cout<<sum<<endl; 
	return 0;
}

发布了30 篇原创文章 · 获赞 67 · 访问量 3055

猜你喜欢

转载自blog.csdn.net/the_ZED/article/details/100116611