题意:将正n边形的n个顶点用n种颜色染色,问有多少种方案(答案mod p,且可由旋转互相得到的算一种)
可以看一下挑战程序,里面有一个更加泛用的推导。有两点需要注意的地方
1.题目中n为10^9,一开始被吓到了,觉得如果d=1时,n=10^9,那不是就意味着要求那么大的欧拉函数了么,这素数一稿,时间绝对超了,后来发现其实只要获得根号n的大小素数就行了,除掉前面素数之后如果还大于1,那必定就是素数了,把这个素数也算进欧拉函数计算里
2.欧拉函数参数为1时取值为0,即当n==d,即k==n时,不存在这种情况,因0<=k<=n-1
先说说Pólya定理
设Q是n个对象的一个置换群,用m种颜色涂染这n个对象,一个对象涂任意一种颜色,则在Q作用下不等价的方案数为:
|Q|为置换群中置换的个数,为将置换q表示成不相杂的轮换的个数,其中包括单轮换,m为颜色数。
分析可以知道本题方案的表达式为:
#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 40000
using namespace std;
bool prime[MAXN];
int pr[MAXN],cnt,n,mod,phi[MAXN];
void isprime()
{
cnt=0;
memset(prime,false,sizeof(prime));
for(int i=2;i<=MAXN-10;i++)
{
if(!prime[i])
{
pr[cnt++]=i;
for(int j=i+i;j<=MAXN-10;j+=i)
{
prime[j]=true;
}
}
}
}
int quick_mul(int n,int d)
{
int ans=1;
n%=mod;
while(d)
{
if(d&1)
{
ans=ans*n%mod;
d--;
}
d>>=1;
n=n*n%mod;
}
return ans;
}
int eular(int x)
{
int ans=x;
for(int i=0;pr[i]*pr[i]<=x;i++)//所有大小只要设平方根n即可
{
//cout<<endl<<x<<" "<<pr[i]<<endl;
if(x%pr[i]==0)
{
ans=ans-ans/pr[i];
while(x%pr[i]==0)
{
x/=pr[i];
//cout<<1;
}
}
}
if(x>1)
ans=ans-ans/x;
return ans%mod;
}
int main()
{
//FILE *fp=fopen("t.txt","r");
int x;
scanf("%d",&x);
isprime();
while(x--)
{
int ans=0;
scanf("%d%d",&n,&mod);
for(int i=1;i*i<=n;i++)
{
//cout<<1;
if(i*i==n)
{
ans+=quick_mul(n,i-1)*eular(i)%mod;
//cout<<i;
ans%=mod;
}else if(n%i==0)
{
ans+=quick_mul(n,n/i-1)*eular(i)%mod+quick_mul(n,i-1)*eular(n/i)%mod;
ans%=mod;
}
}
printf("%d\n",ans%mod);
}
return 0;
}