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); }
Copyright:http://www.cnblogs.com/candy99/