题目大意: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); }
时间: 2024-10-12 20:08:15