题目意思是:有n只青蛙,m个石头0到m-1,每只青蛙初始在0号石头上面,每只青蛙能跳a[i]步,问所有被至少一只青蛙跳到的石头的下表总和。
解题思路:首先对于每只青蛙他可以跳到的石头应该是gcd(a[i],m)的倍数的石头,但是如果分开来求肯定会有重复,所以我们采用容斥原理。所以我们先找出所有m的约数,然后再将每只青蛙能跳到的约数标记为1.用vis数组,因为有重复,所以我们用一个num数组来记录这个约数已经被贡献了几次。刚开始num全为0表示都没贡献过。开始模拟时,从小的约数开始,每个约数还能进行贡献的值为
vis[i]-num[i]在从1到k所以是k*(k-1) 其中k是m除以当前约数累加上这个值后,就要对num[j](p[j]%p[i]==0) 的贡献度进行加上。
PS:num[i]可能比vis[i]大
vis[i] p[i]需要的贡献度
num[i] p[i]当前的贡献度实时更新
#include
using namespace std;
long long a[11111];
long long pp[22222];
long long vis[22222];
long long num[22222];
int main()
{
long long t;
long long n,m;
long long kase=0;
scanf("%lld",&t);
while(t--) {
memset(vis,0,sizeof vis);
memset(num,0,sizeof num);
scanf("%lld%lld",&n,&m);
long long nn=0;
for(long long i=1;i<=sqrt(m);i++) {
if(m%i==0) {
pp[++nn]=i;
if(i*i!=m) {
pp[++nn]=m/i;
}
}
}
sort(pp+1,pp+nn+1);
for(long long i=1;i<=n;i++) {
scanf("%lld",&a[i]);
long long tt=__gcd(a[i],m);
for(int j=1;j<=nn;j++) {
if(pp[j]%tt==0) {
vis[j]=1;
}
}
}
vis[nn]=0;
long long ans=0;
for(long long i=1;i<=nn;i++) {
if(vis[i]!=num[i]) {
long long ss=m/pp[i];
long long sss=vis[i]-num[i];
ans+=ss*(ss-1)/2*pp[i]*sss;
for(long long j=i;j<=nn;j++) {
if(pp[j]%pp[i]==0)
num[j]+=sss;
}
}
}
printf("Case #%lld: %lld\n",++kase,ans);
}
return 0;
}