首页 > 代码库 > bzoj1004: [HNOI2008]Cards(burnside引理+DP)
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)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。