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 }
View Code


推荐文章:

陈瑜希 《Pólya计数法的应用》

符文杰 《Pólya原理及其应用》

 

posted @ 2015-02-16 16:18  idy002  阅读(257)  评论(0编辑  收藏  举报