polya计数+eular函数
输入n,p;(n<=1e9,p<=30000);n个珠子行成一个圆链,有n种颜色的珠子,各种颜色都为无穷大,旋转后一样的珠子为同一种,求不同链子的总数。
听起来就是普通的polya计数,不过一看n=1e9,太大了没办法枚举。
先说一下polya计数:涉及置换群,m种颜色涂染n个物体。
不同染色方案数L=(m^c(g1)+m^c(g2)+..+m^c(gs))/|G| gs为置换的方法,这题就是旋转1,2..n格,c^(g1)为循环节数,即置换后形成的环的个数;|G|为置换方法总数;这里为n。
然后直接给结论吧:
旋转:旋转k个位置时循环节数为gcd(n,k); m^c(gs)=m^gcd(n,k);
翻转:n为奇数时,共有n个循环节数为n/2+1的循环群;
n为偶数时,共有n/2个循环节数为n/2+1的循环群,n=0,还是0;此时|G|=2*n;
不过这题不考虑翻转的情况。
回到题目,由上可知,这时要旋转n个格即有n种置换方法,有1e9种方法,枚举超时;
我们再看一下计算公式:L=(n^gcd(n,1)+n^gcd(n,2)+..+n^gcd(n,n));枚举k共要n次,可
认真看一下,我们可以知道,gcd的值的种数是比较小的<=n的因子个数,gcd必定是n的因子。
然后我们开始想办法枚举gcd值,并计算各个gcd值的个数。
已知n=gcd*x,k=gcd*y;x,y必互质,否知gcd并不是真的最大公约数。所以1<=y<=x;即gcd(n,k)中为gcd值的
个数即为y的种数,即欧拉函数值eular(x),1<=y<=x的中与x互质的个数。
得出这个结论后就可以枚举gcd值了,ans+=pow(n,gcd)*eular(n/gcd=x);有gcd必有n/gcd,
pow(n,n/gcd)*eular(gcd),然后枚举到n^1/2即可。结果要模。
注:题中pow(n,gcd-1)是pow(n,gcd)/|G|,|G|提前除去了。。
#include<stdio.h> #include<string.h> #include<math.h> #define N 50050 #define M 1e9 int num[N],prime[N],k=0; int quickpow(int m,int n,int k) { int b=1; m=m%k; while (n>0) { if (n&1) b=(b*m)%k; n>>=1; m=(m*m)%k; } return b%k; } int eular(int n,int mod) { int i=0,ans=1; for (i=0;prime[i]*prime[i]<=n;++i) { if (n%prime[i]!=0)continue; ans*=prime[i]-1; n/=prime[i]; while (n%prime[i]==0) { ans*=prime[i]; n/=prime[i]; } } if (n>1) ans*=(n-1); return ans%mod; } int main () { int x,n,p,ans; int i,j; memset(num,0,sizeof(num)); for (i=2;i<=1000;++i) for (j=i;i*j<=50000;++j) num[i*j]=1; for (i=2;i<50000;++i) if (!num[i])prime[k++]=i; scanf("%d",&x); while (--x>=0) { ans=0; scanf("%d%d",&n,&p); for (i=1;i*i<=n;++i) { if (i*i==n) ans+=(quickpow(n,i-1,p)*eular(i,p))%p; else if (n%i==0) ans+=(quickpow(n,n/i-1,p)*eular(i,p)+quickpow(n,i-1,p)*eular(n/i,p))%p; ans%=p; } printf("%d\n",ans); } return 0; }