n种珠子n种颜色,要求不同的涂色方案,考虑旋转置换,不考虑翻转置换,结果模除p。
置换考虑用Polya定理写(Burnside引理与Polya定理知识点),复杂度达到O(n),这里的n太大,直接用会超时。
cnt = gcd(k,n) 表示第k种置换的循环节个数
L = n / cnt 表示每个循环节的长度
则 k = cnt * t
cnt = gcd(cnt * t , n)=cnt * gcd(t , n/cnt) ==> gcd (t , n/cnt)=1 ==> gcd (t , L)=1
所以求有多少个gcd(k,n)就是求n / cnt的欧拉函数多大。欧拉是和质因子有关,可以预先质因子打表
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+9;
int p[N],prim[N],len; //p存最小质因子,prim存质数
int eul[N];
void get_prime(){ //素数打表
len=0;
memset(p,0,sizeof(p));
for(int i=2;i<N;i++){
if(!p[i])
prim[len++]=i;
for(int j=0;j<len&&i*prim[j]<N&&(prim[j]<=p[i]||p[i]==0);j++){
p[i*prim[j]]=prim[j]; //找合数的质因子
}
// printf("p[%d]=%d\n",i,p[i]); //i为质数,p[i]为零 ; i为合数,p[i]为其最小质因子
}
// for(int i=0;i<20;i++)
// printf("prim[%d]=%d\n",i,prim[i]);
}
void Euler(){ //欧拉打表
eul[1]=1;
for(int i=2;i<N;i++){
if(!p[i]) //质数的欧拉是质数-1
eul[i]=i-1;
else{
int k=i/p[i];
if(k%p[i]==0) //k存在质因子p[i]
eul[i]=eul[k]*p[i];
else
eul[i]=eul[k]*(p[i]-1);
}
}
}
int Euler(int n,int mod){ //n的欧拉
if(n<N)
return eul[n]%mod;
int ans=n;
for(int i=0;i<len&&prim[i]*prim[i]<=n;i++){
if(n%prim[i]==0){
ans=ans-ans/prim[i];
while(n%prim[i]==0)
n/=prim[i];
}
}
if(n>1)
ans=ans-ans/n;
return ans%mod;
}
int quick_pow(int a,int b,int mod){ //快速幂
int ans=1;
a=a%mod;
while(b){
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int Polya(int n,int mod){ //Polya定理
int ans=0;
for(int L=1;L*L<=n;L++){
if(n%L)
continue;
ans=(ans+Euler(L,mod)*quick_pow(n,n/L-1,mod))%mod;
if(L*L==n)
break;
ans=(ans+Euler(n/L,mod)*quick_pow(n,L-1,mod))%mod;
}
return ans;
}
int main(){
get_prime();
Euler();
int t;scanf("%d",&t);
int n,p;
while(t--){
scanf("%d %d",&n,&p);
printf("%d\n",Polya(n,p));
}
return 0;
}