SGU 282 Isomorphism(polya)

来源:互联网 发布:js鞋子是什么牌子 编辑:程序博客网 时间:2024/05/21 14:32

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 1111typedef 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;}
0 0
原创粉丝点击