zoukankan      html  css  js  c++  java
  • 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原理及其应用》

  • 相关阅读:
    进程池,线程池,协程,gevent模块,协程实现单线程服务端与多线程客户端通信,IO模型
    线程相关 GIL queue event 死锁与递归锁 信号量l
    生产者消费者模型 线程相关
    进程的开启方式 进程的join方法 进程间的内存隔离 其他相关方法 守护进程 互斥锁
    udp协议 及相关 利用tcp上传文件 socketserver服务
    socket套接字 tcp协议下的粘包处理
    常用模块的完善 random shutil shevle 三流 logging
    day 29 元类
    Django入门
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/idy002/p/4294245.html
Copyright © 2011-2022 走看看