[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 namespacestd; 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 }
[BZOJ1004][HNOI2008]Cards 群論+置換群+DP