题目链接:http://www.dotcpp.com/oj/problem1443.html(最近蓝桥一直举办校内赛可能服务器压力大卡的不行,而且这个网站没登陆也能查看题面比较方便一点)
先看到数据规模,如果纯暴力的话10^6 * 10^6肯定是要超时的,所以我们要想办法优化,因为我们只关心栋栋报出的数字总和,我们又知道了其他人报的规律是前面一个人的+1 +2 +3 +4这样报的,那么换句话来说,我们只要知道每循环一组总共加了多少,把这些总和加起来加到栋栋上次报的数字上,就能知道栋栋这次报的数字就多少了,再换句话说就是只需要等差数列求和就得出来了。
不考虑k的情况,假设有3个人报数
假定要知道第二个报的情况,只需要把这一轮的等差数列求和加到上次报的数字上就好了
然后忘记了等差数列的求和公式是什么,就开始推,然后就想了一下比较经典的1+2+3+…+100那个,大概就推到首尾项相加乘个数除以二,得出
n
(
a
n
+
a
1
)
/
2
n(a_n+a_1)/2
n(an+a1)/2,最后考虑k的情况,因为每次要报到k的时候从0开始报,实质上就是对k取余,这也是写这篇博客的原因,因为大家都知道(a*b)%k=(a%k+b%k)%k,然后套上去的时候我一不小心对
a
n
和
a
1
a_n和a_1
an和a1也取余了,结果疯狂WA,而且还能过部分数据不是全WA,当时还以为是我的求和公式推错了,上网搜了一下发现也没用我这种式子的(再次吐槽一下,好多人真的是复制粘贴代码就发博客啊),那时候刚好在和一个朋友聊天,然后朋友也说这个式子是可行的
最后就发现了是我手贱多打了两个取余,当然,至于
(
n
(
a
1
%
k
+
a
n
%
k
)
/
2
)
%
k
!
=
S
n
%
k
(n(a_1\%k+a_n\%k)/2)\%k!=S_n\%k
(n(a1%k+an%k)/2)%k!=Sn%k这里也是能通过数学证明的,这里就不详细展开了。
最后发一下AC代码,这里写的稍微“啰嗦了点”,本来很多能省略的,例如
a
n
a_n
an是能通过
a
1
a_1
a1推出来的,不过我觉得这样写也增加了可读性,更符合数学公式
#include<iostream>
using namespace std;
long long ans=1,next_num=1,total,an,a1;
int main()
{
int n,k,t;
cin>>n>>k>>t;
ans=1;
an=n;
a1=1;
for(int i=1;i<t;i++)
{
next_num=(next_num+(n*(an+a1)/2)%k)%k;
ans+=next_num;
a1=an+1;
an=an+n;
}
cout<<ans;
return 0;
}