BZOJ 1004: [HNOI2008]Cards [Polya 生成函数DP]

传送门

题意:三种颜色,规定使用每种颜色次数$r,g,b$,给出一个置换群,求多少种不等价着色

$m \le 60,\ r,g,b \le 20$


 

咦,规定次数?

《组合数学》上不是有生成函数做法吗....

生成函数貌似可以和背包$DP$互相转换来着

然后就做出来了

每种置换求循环,$d[i][j][k][l]$表示前$i$个循环有了$j$个红$k$个绿$l$个蓝

 

遇到一点小问题,一直输出$0$

看了黄学长的代码发现他加了一个恒等置换....

想了一会儿才明白题目给的不是置换群,因为少了一个恒等置换.....

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=65;
typedef long long ll;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}

int n,r,b,g,m,P,a[N];
int f[N],d[N][21][21],w[N],ans;
bool vis[N];
inline void mod(int &x){if(x>=P) x-=P;}
void dp(){
    int s=0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) if(!vis[i]){
        int u=a[i],len=1;
        while(u!=i) vis[u]=1,len++,u=a[u];
        w[++s]=len;
    }
    memset(d,0,sizeof(d));
    d[0][0][0]=1;
    for(int i=1;i<=s;i++)
        for(int j=r;j>=0;j--)
            for(int k=g;k>=0;k--)
                for(int l=b;l>=0;l--){
                    if(j>=w[i]) mod(d[j][k][l]+=d[j-w[i]][k][l]);
                    if(k>=w[i]) mod(d[j][k][l]+=d[j][k-w[i]][l]);
                    if(l>=w[i]) mod(d[j][k][l]+=d[j][k][l-w[i]]);
                }
    mod(ans+=d[r][g][b]);
}
inline int Pow(int a,int b){
    int re=1;
    for(;b;b>>=1,a=a*a%P)
        if(b&1) re=re*a%P;
    return re;
}
inline int Inv(int a){return Pow(a,P-2);}
int main(){
    freopen("in","r",stdin);
    r=read();b=read();g=read();m=read();P=read();
    n=r+b+g;
    for(int j=1;j<=m;j++){
        for(int i=1;i<=n;i++) a[i]=read();
        dp();
    }
    m++;
    for(int i=1;i<=n;i++) a[i]=i;
    dp();
    ans=ans*Inv(m)%P;
    printf("%d",ans);
}

 

posted @ 2017-02-28 16:20  Candy?  阅读(560)  评论(0编辑  收藏  举报