題意:將正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;
}