Description
用m种颜色对一个n阶完全图染色,若一张图的节点经过重排后变成另一张图则称两张图同构,问一共有多少种不同构的染色方案,结果模p
Input
三个整数n,m,p
Output
输出染色方案数,结果模p(1≤n≤53, 1≤m≤1000)
Sample Input
1 1 2
Sample Output
1
Solution
polya,首先找置换,n个点的图有n!个点置换,暴力枚举显然不行,所以可以把n分解成若干个环,由于n<=53所以这种组合不会太多,大约是10^6级别,对于每一种组合n=c1*n1+c2*n2+…+ck*nk(ci为元素数量为ni的环的个数),n个节点全排列有n!种方案,其中每个环i被重复计算了ni!次,固定一个点剩下的点有(ni-1)!种排列情况,所以一个环重复计算了ni次,对于有相同元素数量的环也重复计算了ci!次,故这种组合对应的置换群个数为n!/(n1^c1*n2^c2*…nk^ck*c1!*c2!…*ck!)。然后求每一种置换的轮换个数,而对于每一种组合,还需求其边置换的轮换个数,对于在同一个环i上的两个点,显然其轮换个数为ni/2个,而对于不在同一个环上的两点a,b(不妨设a在环i上b在环j上),ab边至少需要经过lcm(ni,nj)次置换才能变回ab,所以此时轮换个数为ni*nj/lcm(ni,nj)=gcd(ni,nj)。我们已经知道n的每一种组合对应的置换个数以及每一种置换对应的轮换个数,之后套polya定理即可求解。
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 1111
typedef long long ll;
ll num[maxn],cnt[maxn],res,factor[maxn],n,m,p,ans;
void init()
{
ans=res=0;
factor[0]=1;
for(int i=1;i<maxn;i++)
factor[i]=factor[i-1]*i%p;
}
ll gcd(ll a,ll b)
{
if(a<b)return gcd(b,a);
if(b==0)return a;
return gcd(b,a%b);
}
ll mod_pow(ll a,ll b,ll p)
{
ll ans=1ll;
a%=p;
while(b)
{
if(b&1) ans=(ans*a)%p;
a=(a*a)%p;
b>>=1;
}
return ans;
}
void dfs(ll now,ll left)
{
if(left==0)
{
ll a=1,b=0;
for(ll i=0;i<res;i++)
{
a=a*mod_pow(num[i],cnt[i],p)%p*factor[cnt[i]]%p;
b+=cnt[i]*(cnt[i]-1)/2*num[i]+num[i]/2*cnt[i];
for(ll j=i+1;j<res;j++)
b+=cnt[i]*cnt[j]*gcd(num[i],num[j]);
}
a=mod_pow(a,p-2,p)*factor[n]%p;
ans=(ans+a*mod_pow(m,b,p)%p)%p;
}
if(now>left)return ;
dfs(now+1,left);
for(ll i=1;i*now<=left;i++)
{
num[res]=now,cnt[res++]=i;
dfs(now+1,left-i*now);
res--;
}
}
int main()
{
while(~scanf("%lld%lld%lld",&n,&m,&p))
{
init();
dfs(1,n);
ans=ans*mod_pow(factor[n],p-2,p)%p;
printf("%lld\n",ans);
}
return 0;
}