SGU 282 Isomorphism(polya)

13 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值