[BZOJ1004][HNOI2008]Cards(置换群+背包+乘法逆元)

245 篇文章 0 订阅
13 篇文章 0 订阅

题目描述

传送门

题解

推荐一个课件:http://wenku.baidu.com/link?url=iT9AF_F7nlm5ChUKKgVHCTZXJJIlRvmqxebDvwClLNWVVz84HtZT6Z7Clmo-ABxqJfct5I6bOnEf4jiaMqgke9ZEJMCPHRi2-KEq-eQQSCS 这里解释了Burnside引理和Polya定理
转自黄学长的题解:
Burnside定理:有m个置换k种颜色,所有本质不同的染色方案数就是每种置换的不变元素的个数的平均数。所谓不变元素就是一种染色方案经过置换变换后和没变化之前一样。所以现在就是需要求出不变元素,同时还要满足各种颜色数量的限制。置换的循环在不变元素中一定是一个颜色,然后可以求一个三维的01背包的方案数。而最后的除法需要利用扩展欧几里得求乘法的逆元。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=25;
const int max_m=65;
const int max_N=max_n*3;

int n,m,sr,sb,sg,Mod,x,ans;
int a[max_m][max_N],sum[max_N],f[max_n][max_n][max_n];
bool used[max_N];

inline int dp(int x)
{
    int p,cnt=0;
    memset(used,0,sizeof(used));
    for (int i=1;i<=n;++i)
        if (!used[i])
        {
            p=i;
            sum[++cnt]=1;
            used[p]=true;
            while (!used[a[x][p]])
            {
                sum[cnt]++;
                used[a[x][p]]=true;
                p=a[x][p];              
            }
        }
    for (int i=0;i<=sr;++i)
        for (int j=0;j<=sb;++j)
            for (int k=0;k<=sg;++k)
                f[i][j][k]=0;
    f[0][0][0]=1;
    for (int h=1;h<=cnt;++h)
        for (int i=sr;i>=0;--i)
            for (int j=sb;j>=0;--j)
                for (int k=sg;k>=0;--k)
                {
                    if (i>=sum[h]) f[i][j][k]=(f[i][j][k]+f[i-sum[h]][j][k])%Mod;
                    if (j>=sum[h]) f[i][j][k]=(f[i][j][k]+f[i][j-sum[h]][k])%Mod;
                    if (k>=sum[h]) f[i][j][k]=(f[i][j][k]+f[i][j][k-sum[h]])%Mod;
                }
    return f[sr][sb][sg];               
}
inline int fast_pow(int a,int p)
{
    int ans=1;
    for (;p;p>>=1,a=(a*a)%Mod)
        if (p&1)
            ans=(ans*a)%Mod;
    return ans;
}
int main()
{
    scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&Mod);
    n=sr+sb+sg;
    for (int i=1;i<=m;++i)
        for (int j=1;j<=n;++j)
            scanf("%d",&a[i][j]);
    m++;
    for (int i=1;i<=n;++i) a[m][i]=i;
    for (int i=1;i<=m;++i)
        ans=(ans+dp(i))%Mod;
    x=fast_pow(m,Mod-2);
    ans=(ans*x)%Mod;
    printf("%d\n",ans);
}

总结

注意一下整数群和置换群的区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值