bzoj1004: [HNOI2008]Cards(burnside引理+DP)
Posted Sakits
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1004: [HNOI2008]Cards(burnside引理+DP)相关的知识,希望对你有一定的参考价值。
题目大意:3种颜色,每种染si个,有m个置换,求所有本质不同的染色方案数。
置换群的burnside引理,还有个Pólya过几天再看看。。。
burnside引理:有m个置换k种颜色,所有本质不同的染色方案数就是每种置换的不变元素的个数的平均数。
求每种置换的不变元素的个数用背包解决。因为置换之后元素不变,所以对于每个循环节我们要染一个颜色,于是先处理出循环节作为背包中的“物体”,然后一个三维背包解决。f[i][j][k]的i j k表示三种颜色分别还可以染多少次。
除m%p用费马小定理就行了,我才不用exGCD...(QAQ因为老是忘记怎么写,快速幂多资磁
没清零WA了2次。。。最近老是出小问题
#include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #define ll long long using namespace std; void read(ll &k) { k=0;int f=1;char c=getchar(); while(c<‘0‘||c>‘9‘)c==‘-‘&&(f=-1),c=getchar(); while(c<=‘9‘&&c>=‘0‘)k=k*10+c-‘0‘,c=getchar(); k*=f; } ll sr,sb,sg,m,p,n,ans; ll next[110],a[70][110],d[110],f[2][70][70][70]; ll dp(int x) { int cnt=0; for(int i=1;i<=n;i++)next[i]=0; for(int i=1;i<=n;i++) if(!next[i]) { d[++cnt]=next[i]=1; int p=i; while(!next[a[x][p]]) { p=a[x][p]; next[p]=1; d[cnt]++; } } for(int i=0;i<=sr;i++) for(int j=0;j<=sb;j++) for(int k=0;k<=sg;k++) f[1][i][j][k]=f[0][i][j][k]=0; f[1][0][0][0]=1; int now=0; for(int l=1;l<=cnt;l++) { for(int i=0;i<=sr;i++) for(int j=0;j<=sb;j++) for(int k=0;k<=sg;k++) { if(i>=d[l])f[now][i][j][k]=(f[now^1][i-d[l]][j][k]+f[now][i][j][k])%p; if(j>=d[l])f[now][i][j][k]=(f[now^1][i][j-d[l]][k]+f[now][i][j][k])%p; if(k>=d[l])f[now][i][j][k]=(f[now^1][i][j][k-d[l]]+f[now][i][j][k])%p; } now^=1; } return f[now^1][sr][sb][sg]; } ll mi(ll a,int b) { ll t=1,y=a; while(b) { if(b&1)t=(t*y)%p; y=(y*y)%p; b>>=1; } return t%p; } int main() { read(sr);read(sb);read(sg);read(m);read(p);n=sr+sb+sg; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) read(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))%p; printf("%lld\n",ans*mi(m,p-2)%p); }
以上是关于bzoj1004: [HNOI2008]Cards(burnside引理+DP)的主要内容,如果未能解决你的问题,请参考以下文章