题意:
有一个0-m-1的环,有n只青蛙从0这个点开始跳,问哪些点可以被跳到
分析:
经分析我们可以知道,一个点如果可以被跳到,那么他一定为gcd(x,m)的倍数,如果直接把这些点相加,显然,某些点可能被加了2次甚至更多
很容易想到容斥,但是怎么容斥又是一个难点
假设num[i]是能够被跳到的点,那么num[i]的倍速一定能够跳到,这会形成一个等差数列,.利用求和公式便可知道,
但是到了num[i]*2的时候,这个数列的后面几项又会被重新算一遍,所以只需要vis[j]-vis[i],意思便是多退少补,如果前面减得多了,那么vis[j]便会是正数,
反之为负数
#include<bits/stdc++.h>
using namespace std;
const int maxn=100000;
int vis[maxn],num[maxn],cnt;
int gcd(int x,int y){
if(x<y)
swap(x,y);
if(y==0)
return x;
return gcd(y,x%y);
}
void init(int m){
memset(vis,0,sizeof(vis));
cnt=0;
num[cnt++]=1;
for(int i=2;i*i<=m;i++){
if(m%i==0){
if(i*i==m)
num[cnt++]=i;
else{
num[cnt++]=i;
num[cnt++]=m/i;
}
}
}
}
int main(){
int _,n,m,x;
scanf("%d",&_);
for(int case1=1;case1<=_;case1++){
scanf("%d%d",&n,&m);
init(m);
sort(num,num+cnt);
for(int i=1;i<=n;i++){
scanf("%d",&x);
x=gcd(x,m);
for(int j=0;j<cnt;j++){
if(num[j]%x==0)
vis[j]=1;
}
}
__int64 ans=0;
for(int i=0;i<cnt;i++){
if(vis[i]!=0){
__int64 tmp=m/num[i];
ans+=(tmp-1)*tmp/2*num[i]*vis[i];
for(int j=i+1;j<cnt;j++)
if(num[j]%num[i]==0)
vis[j]-=vis[i];
}
}
printf("Case #%d: %I64d\n",case1,ans);
}
return 0;
}