题意:
有n个青蛙,一开始都在0点,然后有m个石子圈成一圈的石子,石子的编号是从0-m-1的然后青蛙只能顺时针跳,每个青蛙可以一次跳a[i]格,然后所有青蛙都这样一直跳下去然后问你,这些青蛙踩过的石子的编号和是多少?
思路:
啊我这只弱鸡,,,只能照着大神的思路写下来,,然后就基本一样了。。。
对与第i只青蛙,它必然会跳到
d=k∗gcd(a[i],m)
这个位置上,
(可以通过列模线性方程的有解条件得到(贝祖定理))
如果直接暴力枚举的话显然会超时。所以需要换种思路,通过观察上式我们发现,
d一定是m的因子,所以我们就可以通过枚举m的所有因子来找到所有间隔d。
然后从m的第一个因子开始计数,同时记录这个数对之后他的倍数的影响,
如果这里加多了,后边的位置就要相应加上,好方便之后减去。(容斥原理)
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <string.h>
typedef long long int lli;
using namespace std;
int vis[100100];
int factor[100100];
int num[100100];
lli gcd(lli a,lli b){
return b==0? a:gcd(b,a%b);
}
int main()
{
int t;
cin>>t;
int n,m;
int cas = 0;
while(t--){
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
cas++;
int cnt = 0;
scanf("%d%d",&n,&m);
lli temp = sqrt(m);
for(int i = 1;i <= temp;i++){
if(m % i == 0){
factor[cnt++] = i;
if(i * i != m){
factor[cnt++] = m/i;
}
}
}
sort(factor,factor+cnt);
for(int i = 0;i < n;i++){
scanf("%I64d",&temp);
temp = gcd(temp,m);
for(int j = 0;j < cnt;j++){
if(factor[j] % temp == 0){
vis[j] = 1;
}
}
}
vis[cnt-1] = 0;
lli ans = 0;
m -= 1;
for(int i = 0;i < cnt-1;i++){
if(num[i] != vis[i]){
temp = m/factor[i];
// factor[i] * (temp+1) * temp/2 是等差数列求和
ans += factor[i] * (temp+1) * temp/2 * (vis[i]-num[i]);
temp = vis[i] - num[i];// 关键
for(int j = i;j < cnt-1;j++){
if(factor[j] % factor[i] == 0){
num[j] += temp;//关键
}
}
}
}
printf("Case #%d: %I64d\n",cas,ans);
}
return 0;
}