Description

小胡同学是个热爱运动的好孩子。
每天晚上,小胡都会去操场上跑步,学校的操场可以看成一个由n 个格子排成的一个环形,格子按照顺时针顺序从0 到n-1 标号。
小胡观察到有m 个同学在跑步,最开始每个同学都在起点(即0 号格子),每个同学都有个步长ai,每跑一步,每个同学都会往顺时针方向前进ai 个格子。由于跑道是环形的,如果

一个同学站在n -1 这个格子上,如果他前进一个格子,他就会来到0。
他们就这样在跑道上上不知疲倦地跑呀跑呀。小胡同学惊奇地发现,似乎有些格子永远不会被同学跑到,他想知道这些永远不会被任何一个同学跑到的格子的数目,你能帮帮他
吗?(我们假定所有同学都跑到过0 号格子)。

Solution

比赛的时候只想出了一个结论:如果一个格子是gcd(ai,n)的倍数,那么这个格子能被走到。
证明:设x是格子,那么ax+b≡0(modn),那么就相当于就一个方程ax+b=ny。
所以ax−ny=−b,用扩展gcd求出ax−ny=gcd(a,n)的解,那么b一定要是gcd(a,n)是d的倍数才有解。
得证。
那么现在要不重不漏的去计算所有的格子。那么就是求满足gcd(p[i],n)|d
枚举每一个n的约数p[i],然后求l=gcd(p[i],n),我们不需要求有多少个数是l的倍数,我们只需要知道有多少个数等于l就好了(因为分解出的约数会概括l的所有倍数,所以只用单层的管这个l)。
那么现在就要求有多少个数满足gcd(i,n)=l
那么就相当于求有多少个数满足gcd(il,nl)=1,那么就是有多少个数与nl互质,就有φ(nl)个。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1000007;
int i,j,k,l,t,n,m,ans,d;
int a[57],p[maxn];
bool bz;
int gcd(int x,int y){return(!y)?x:gcd(y,x%y);}
int phi(int x){
int i,y=x,z=x;
fo(i,1,p[0]){
if(p[i]==1)continue;
if(x%p[i]==0)y=y/p[i]*(p[i]-1);
while(x%p[i]==0)x/=p[i];
if(x==1)break;
}
return y;
}
int main(){
// freopen("","r",stdin);
// freopen("running.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m)scanf("%d",&a[i]);
p[0]=0;
fo(i,1,sqrt(n)){
if(n%i==0){
p[++p[0]]=i;
if(n/i==i)continue;
p[++p[0]]=n/i;
}
}
sort(p+1,p+1+p[0]);
fo(k,1,p[0]){
i=p[k];
bz=0;
fo(j,1,m){
d=i;
l=gcd(a[j],n);
if(d%l==0){
bz=1;
break;
}
}
if(bz)ans+=phi(n/i);
}
printf("%d\n",n-ans);
}