Polya计数法原理可以看符文杰的集训队论文<<Polya原理及其应用>>、题目可以看陈瑜希的论文<<Polya计数法的应用>>
解题三步骤:
1、确定置换群(注意考虑周全)
2、计算每个置换下的循环节数目
3、带入公式
有时候置换数目太多,而许多置换具有相同的循环节数目,可以统一起来考虑,进行优化。
下面是三道例题,难度递增,第一题是裸题,后面两题都用到了上面说的优化。
1、POJ2409
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n,m;
ll GCD(ll a,ll b)
{
if(b==0)
return a;
return GCD(b,a%b);
}
ll Pow(ll a,ll b)
{
ll ret=1,c=a;
while(b)
{
if(b&1)
ret*=c;
c*=c;
b>>=1;
}
return ret;
}
int main()
{
ll i,j,ans;
while(cin>>m>>n)
{
if(n==0&&m==0)
break;
ans=0;
for(i=0;i<n;++i)
ans+=Pow(m,GCD(n,i));
if(n&1)
ans+=n*Pow(m,n/2+1);
else
{
ans+=n/2*Pow(m,n/2+1);
ans+=n/2*Pow(m,n/2);
}
cout<<ans/(2*n)<<endl;
}
return 0;
}
2、POJ2154
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
//typedef long long LL;
int Prime[36000]; //一个数n大于sqrt(n)的质因子最多只有一个
bool Flag[36000];
int tot;
void Get_Prime_Table()
{
tot=0;
memset(Flag,false,sizeof(Flag));
int i,j;
for(i=2;i<36000;i++)
{
if(!Flag[i])
{
for(j=2*i;j<36000;j+=i)
Flag[j]=true;
}
}
for(i=2;i<36000;++i)
if(!Flag[i])
Prime[tot++]=i;
// cout<<tot<<endl;
}
int Eular(int n,int p)
{
int ret=n,i;
for(i=0;i<tot&&Prime[i]*Prime[i]<=n;++i)
{
if(n%Prime[i]==0)
{
ret-=ret/Prime[i];
while(n%Prime[i]==0)
n/=Prime[i];
}
}
if(n!=1)
ret-=ret/n;
return ret%p;
}
int Pow(int a,int b,int p)
{
int ret=1,c=a%p;
while(b)
{
if(b&1)
(ret*=c)%=p;
(c*=c)%=p;
b>>=1;
}
return ret;
}
int main()
{
int X;
int N,P,i,ans;
scanf("%d",&X);
Get_Prime_Table();
while(X--)
{
scanf("%d %d",&N,&P);
ans=0;
for(i=1;i*i<=N;++i)
{
if(i*i==N)
{
// cout<<i<<endl;
cout<<Eular(i,P)<<endl;
// cout<<"................."<<endl;
(ans+=Eular(i,P)*Pow(N,i-1,P))%=P;
}
else if(N%i==0)
{
// cout<<i<<endl;
// cout<<Eular(i,P)<<endl;
// cout<<Eular(N/i,P)<<endl;
// cout<<"....................."<<endl;
(ans+=Eular(i,P)*Pow(N,N/i-1,P)+Eular(N/i,P)*Pow(N,i-1,P))%=P;
}
}
printf("%d\n",ans);
}
return 0;
}
3、SGU282
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL Mod;
LL L[55],Tot,K[55],N,M,Num,Div;
LL GCD(LL a,LL b)
{
if(b==0)
return a;
return GCD(b,a%b);
}
LL Pow(LL a,LL b)
{
LL c=a%Mod,ret=1;
while(b)
{
if(b&1)
(ret*=c)%=Mod;
(c*=c)%=Mod;
b>>=1;
}
return ret;
}
void Init()
{
Tot=0,Num=1;
memset(K,0,sizeof(K));
LL i;
for(i=2;i<=N;++i)
{
(Num*=i)%=Mod;
}
Div=Pow(Num,Mod-2);
}
LL Fact(LL a)
{
LL i,ret=1;
for(i=2;i<=a;++i)
{
(ret*=i)%=Mod;
}
return ret;
}
LL DFS_Polya(LL remain,LL limit)
{
if(remain==0)
{
LL i,j,t1=1,t2=0;
memset(K,0,sizeof(K));
for(i=0;i<Tot;++i)
{
(t1*=L[i])%=Mod;
t2+=L[i]/2;
K[L[i]]++;
}
for(i=1;i<=N;++i)
{
(t1*=Fact(K[i]))%=Mod;
}
for(i=0;i<Tot;++i)
{
for(j=i+1;j<Tot;++j)
{
t2+=GCD(L[i],L[j]);
}
}
return (((Num*Pow(t1,Mod-2))%Mod)*Pow(M,t2))%Mod;
}
else
{
LL i,j,ret=0;
for(i=1;i<=limit&&i<=remain;++i)
{
L[Tot++]=i;
(ret+=DFS_Polya(remain-i,i))%=Mod;
Tot--;
}
return ret;
}
}
int main()
{
scanf("%I64d %I64d %I64d",&N,&M,&Mod);
Init();
printf("%I64d\n",(DFS_Polya(N,N)*Div)%Mod);
return 0;
}