• 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
    ", ans );
    88 }
    View Code


    推荐文章:

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

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

  • 相关阅读:
    OO第三单元博客作业
    OO第二单元博客作业
    OO第一单元博客作业
    OO第四单元总结
    OO第三次作业总结
    OO第二单元作业总结
    OO第一单元作业总结
    面向对象总结博客
    面向对象第三单元总结博客
    面向对象第二单元总结博客
  • 原文地址:https://www.cnblogs.com/idy002/p/4294245.html
Copyright © 2020-2023  润新知