bzoj 1004 Cards 组合计数
这道题考察的是组合计数(用Burnside,当然也可以认为是Polya的变形,毕竟Polya是Burnside推导出来的)。
这一类问题的本质是计算置换群(A,P)中不动点个数!(所谓不动点,是一个二元组(a,p),a∈A,p∈P ,使得p(a)=a,即a在置换p的作用后还是a)。
Polya定理其实就是告诉了我们一类问题的不动点数的计算方法。
对于Burnside定理的考察,我见过的有以下几种形式(但归根结底还是计算不动点数):
1、限制a(a∈A)的特点,本题即是如此(限制了各颜色个数,可以用DP来统计(以前我们直接用c^k来计算某置换的不动点,c是颜色总数,k是循环节个数))
2、让P的数量高出暴力的范围,(比如经典的项链染色问题,或者P就是一个对称群(包含所有置换)),此时就必须寻找数学规律,不能硬来。项链染色这种可以用欧拉函数优化;对于图的同构计数(它的置换群是一个对称群),先将置换按照其格式分类(见《组合数学》,可以想象具有相同格式的置换的不动点也是相同的),然后再观察某一特定格式的置换的不动点数是多少(是有规律的)。
注:应用定理解题时要保证置换成群,本题中,结合律是肯定的,输入说明中已经说了,该置换集合满足:封闭性,逆元,所以我们自己再添一个单位元((1)(2)...(m))就行了。
1 /************************************************************** 2 Problem: 1004 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:116 ms 7 Memory:844 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <cstring> 12 #define maxn 65 13 using namespace std; 14 15 int mpow( int a, int b, int n ) { 16 a %= n; 17 int rt; 18 for( rt=1; b; b>>=1,a=(a*a)%n ) 19 if( b&1 ) rt=(rt*a)%n; 20 return rt; 21 } 22 int inv( int a, int n ) { 23 return mpow(a,n-2,n); 24 } 25 26 int sp[maxn], tot; 27 bool vis[maxn]; 28 void split( int n, int *a ) { 29 tot = 0; 30 memset( vis, 0, sizeof(vis) ); 31 for( int i=1; i<=n; i++ ) { 32 if( vis[i] ) continue; 33 tot++; 34 sp[tot] = 0; 35 int cur = i; 36 do { 37 sp[tot]++; 38 vis[cur] = true; 39 cur = a[cur]; 40 } while( !vis[cur] ); 41 } 42 } 43 44 int n, sr, sg, sb, m, p, ans; 45 int a[maxn]; 46 int dp[21][21][21]; 47 48 int dodp() { 49 memset( dp, 0, sizeof(dp) ); 50 dp[0][0][0] = 1; 51 for( int i=1; i<=tot; i++ ) { 52 int sz = sp[i]; 53 for( int r=sr; r>=0; r-- ) 54 for( int g=sg; g>=0; g-- ) 55 for( int b=sb; b>=0; b-- ) { 56 int & dv = dp[r][g][b]; 57 dv = 0; 58 if( r>=sz ) dv += dp[r-sz][g][b]; 59 if( g>=sz ) dv += dp[r][g-sz][b]; 60 if( b>=sz ) dv += dp[r][g][b-sz]; 61 dv %= p; 62 } 63 } 64 return dp[sr][sg][sb]; 65 } 66 void update( int n, int *a ) { 67 split(n,a); 68 ans += dodp(); 69 if( ans>p ) ans -= p; 70 } 71 72 int main() { 73 scanf( "%d%d%d%d%d", &sr, &sg, &sb, &m, &p ); 74 n = sr+sg+sb; 75 for( int i=1; i<=m; i++ ) { 76 for( int j=1; j<=n; j++ ) 77 scanf( "%d", a+j ); 78 update(n,a); 79 } 80 81 for( int i=1; i<=n; i++ ) 82 a[i] = i; 83 update(n,a); 84 85 ans *= inv(m+1,p); 86 ans %= p; 87 printf( "%d\n", ans ); 88 }
推荐文章:
陈瑜希 《Pólya计数法的应用》
符文杰 《Pólya原理及其应用》