[BZOJ1004][HNOI2008]Cards 群论+置换群+DP
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1004
首先贴几个群论相关定义和引理。
群:G是一个集合,*是定义在这个集合上的一个运算。
如果满足以下性质,那么(G, *)是一个群。
1)封闭性,对于任意 a, b 属于 G, a * b 属于 G
2)结合律, a * b * c = a * (b * c)
3)单位元,在 G 中存在一个单位元 e ,使得对于 G 中任意的 a , a * e = e * a = a
4)逆元, 对于 G 中任意的 a ,在 G 中存在 b , 使得 a * b = e , 其中 b 叫做 a 的逆元
比如在模一个数意义下的整数加法就是一个群。
满足交换律的群是交换群,又叫阿贝尔群。
置换:可以用 (a1 -> b1, a2 -> b2, ... , an -> bn) 表示一个置换,其中 a1, ... , an 和 b1, ..., bn 都是1 到 n 的一个排列;
如果一些置换和它们的叠加运算构成一个群,就把它们叫做一个置换群。
在置换群中的 Burnside 引理:如果按照一定要求,要对1到n 的位置染色,那么本质不同的染色方案数为置换群中每个置换的不动染色方案数的平均数。
来解释以下,本质不同的染色方案是指,两个染色方案不能通过置换群中的任意置换变换使其相同,那么它们就是本质不同的。
某个置换的不动染色方案数是指,用这个置换变换之后没有发生变化的染色方案。
那么我们就是要求出每个置换的不动染色方案数。
Polya定理,如果是用k种颜色染色,那么对于置换 P 来说,它的不动染色方案数为 k^(L(P)), 其中L(P)为置换P的循环节数。
由于这道题中有颜色数目的限制,我们不能直接套Polya定理,但是可以把每个循环节当作一种物品,用背包求方案数。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int inline readint(){ 6 int Num;char ch; 7 while((ch=getchar())<'0'||ch>'9');Num=ch-'0'; 8 while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0'; 9 return Num; 10 } 11 int Sr,Sb,Sg,n,m,mod; 12 int quick_pow(int x,int y){ 13 int base=x,sum=1; 14 while(y){ 15 if(y&1) sum=sum*base%mod; 16 base=base*base%mod; 17 y>>=1; 18 } 19 return sum; 20 } 21 int fm[65][65],cnt[65],f[65/3][65/3][65/3]; 22 bool vis[65]; 23 int dp(int x){ 24 int tot=0; 25 memset(vis,false,sizeof(vis)); 26 memset(cnt,0,sizeof(cnt)); 27 memset(f,0,sizeof(f)); 28 for(int i=1;i<=n;i++){ 29 if(!vis[i]){ 30 int j=i; 31 tot++; 32 while(!vis[fm[x][j]]){ 33 vis[fm[x][j]]=true; 34 cnt[tot]++; 35 j=fm[x][j]; 36 } 37 } 38 } 39 f[0][0][0]=1; 40 for(int i=1;i<=tot;i++) 41 for(int j=Sr;j>=0;j--) 42 for(int k=Sb;k>=0;k--) 43 for(int t=Sg;t>=0;t--){ 44 if(j>=cnt[i]) f[j][k][t]=(f[j][k][t]+f[j-cnt[i]][k][t])%mod; 45 if(k>=cnt[i]) f[j][k][t]=(f[j][k][t]+f[j][k-cnt[i]][t])%mod; 46 if(t>=cnt[i]) f[j][k][t]=(f[j][k][t]+f[j][k][t-cnt[i]])%mod; 47 } 48 return f[Sr][Sb][Sg]; 49 } 50 int main(){ 51 Sr=readint(); 52 Sb=readint(); 53 Sg=readint(); 54 m=readint(); 55 mod=readint(); 56 n=Sr+Sb+Sg; 57 for(int i=1;i<=m;i++) 58 for(int j=1;j<=n;j++) 59 fm[i][j]=readint(); 60 m++; 61 for(int i=1;i<=n;i++) fm[m][i]=i; 62 int ans=0; 63 for(int i=1;i<=m;i++) ans+=dp(i); 64 ans=ans*quick_pow(m,mod-2)%mod; 65 printf("%d\n",ans); 66 return 0; 67 }