[置换群+背包] BZOJ1004: [HNOI2008]Cards

题意

小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红,蓝,绿。
小春发明了M种不同的洗牌法,问Sun有多少种不同的染色方案。
两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种。
输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代
替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。
答案可能很大,只要求出答案除以P的余数(P为质数).

题解

完全裸的一个置换群模型。只需要注意到各种颜色数是有规定的,显然不能直接 3nc(g) 直接得到对某置换g保持不变的的方案数。
那如何做呢?对于某个置换g,若某一着色方案对g保持不变,那么它对应g中的各个循环节中的元素都必须染成同样的颜色。
这样就可以转化成背包求方案数了。设 f[i][j][k] 表示染了i个红色,j个蓝色,k个绿色的方案数。转移就是把某个循环节的元素全部涂成某一种颜色。最后有除法,求一下逆元就好了。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=75;
int allv[3],n,m,MOD,a[maxn][maxn],ans,f[maxn][maxn][maxn],w[maxn];
bool vis[maxn];
int get(int id){
    memset(w,0,sizeof w);
    memset(vis,0,sizeof vis);
    for(int i=1;i<=n;i++) if(!vis[i]){
        w[0]++; int now=i;
        do w[w[0]]++, vis[now]=true, now=a[id][now]; while(!vis[now]);
    }
    memset(f,0,sizeof f); f[0][0][0]=1;
    for(int i=1;i<=n;i++)
     for(int j0=allv[0];j0>=0;j0--)
      for(int j1=allv[1];j1>=0;j1--)
       for(int j2=allv[2];j2>=0;j2--){
            int &fnow=f[j0][j1][j2];
            if(j0>=w[i]) fnow=(fnow+f[j0-w[i]][j1][j2])%MOD;
            if(j1>=w[i]) fnow=(fnow+f[j0][j1-w[i]][j2])%MOD;
            if(j2>=w[i]) fnow=(fnow+f[j0][j1][j2-w[i]])%MOD;
       }
    return f[allv[0]][allv[1]][allv[2]];
}
int power(int a,int b){
    if(!b) return 1;
    if(b&1) return (a*power(a,b-1))%MOD;
    int t=power(a,b/2); return (t*t)%MOD;
}
int main(){
    freopen("bzoj1004.in","r",stdin);
    freopen("bzoj1004.out","w",stdout);
    scanf("%d%d%d%d%d",&allv[0],&allv[1],&allv[2],&m,&MOD); n=allv[0]+allv[1]+allv[2];
    for(int i=1;i<=m;i++)
     for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
    m++; for(int j=1;j<=n;j++) a[m][j]=j;
    for(int i=1;i<=m;i++) ans=(ans+get(i))%MOD;
    ans=(ans*power(m,MOD-2))%MOD;
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值