zoukankan      html  css  js  c++  java
  • bzoj 1004: [HNOI2008]Cards

    一道群论题。。要用到Burnside引理

    这里copy一下别人写的题解好了。。

    介绍一种组合数学中的Pólya计数法、Burnside定理。

    给定一个集合G={a,b,c,…}和集合G上的二元运算,并满足:

    (a) 封闭性:"a,bÎG, $cÎG, a*b=c

    (b) 结合律:"a,b,cÎG, (a*b)*c=a*(b*c)

    (c) 单位元:$eÎG, "aÎG, a*e=e*a=a

    (d) 逆元:"aÎG, $bÎG, a*b=b*a=e,记b=a-1

    则称集合G在运算*之下是一个群,简称G是群。一般a*b简写为ab

    置换

    n个元素1,2,…,n之间的一个置换表示11n中的某个数a1取代,21n中的某个数a2取代,直到n1n中的某个数an取代,且a1,a2,…,an互不相同。

    置换群

    置换群的元素是置换,运算是置换的连接。例如:

    可以验证置换群满足群的四个条件。

    Burnside引理介绍

           下面我们介绍Pólya计数法所要用到的一个引理——Burnside定理。

    D(aj) 表示在置换aj下不变的元素的个数。L表示本质不同的方案数。


    例题:对N个格子进行2色染色(用XE各表示一种颜色)。并且格子可以通过m种置换进行变换。求本质不同的染色法。

    对于N=4的情况。一共有4个置换:

    所有方案在置换a1下都不变,D(a1)=16

    XXXXEEEE在置换a2下不变,D(a2)=2

    XXXXEEEE以及XEXEEXEX在置换a3下不变,D(a3)=4

    XXXXEEEE在置换a4下不变,D(a4)=2

    计算出

    那么,本质不同的染色法为6种。

    置换群的另一种表示法:

    比如:                     =(1 2 4)(3 5)    

     

     

    可以形象地理解成循环节的含义。1-》2,2-》4,4-》1,就可以表示成(1 2 4)

    再对每一个循环节合并成群。

     

    题目分析:

    通过了解置换和置换群,这道Cards想必就很容易分析及理解了。可以把(m + 1)种洗牌法看作是元素个数为(m + 1)的置换群,每种洗牌法都是一个置换。现在就是要求本质不同的染色法。这个和Burnside定理的不谋而合。对于一般的Burnside定理的应用, 对每一个置换都可以枚举元素状态再判断是否元素经变换后不变,然后求出D(aj)。但是显然这样耗时不讨好,所以来看置换的另一表示法有没有突破口。每一个置换可以由T个循环节组成,每一个循环节显然要染成一样的颜色,这样才能保证整体上元素经过变换后不变。这样我们通过简单的DP统计即可完成D(aj)的计算了。

    但是,问题的不同在于,限制了每种颜色的使用次数。srsbsg的限制促进了我们更深一步的思考。针对每一个置换,状态表示为F[u,I,j,k] . u 表示阶段到了第u个循环节,ijk依次表示当前阶段srsbsg各使用了ijk次。根据上面的分析,很容易写出DP转移方程。A[u]表示循环节的循环节长度,显然这里循环节具体是什么已经不重要了。

    F[u,I,j,k] = F[u-1,i-a[u],j,k] + F[u-1,I,j-a[u],k] + F[u-1,I,j,k-a[u]]

    DP时要不断对p取模。还要注意到一个新的问题,我们最后的答案是要求,每一个置换的F数组的答案之和除以置换的总数。显然不能直接做除法。

    对于A / B p取模,可以这么做。

    A / B mod p = A * C mod p , B * C mod p = 1

    这里因为m + 1 很小,所以直接枚举C的值即可。更广泛的使用的话,便要用到扩展欧几里德定理。这里不做阐述。

     

    至此,Cards圆满解决。

      1 /*
      2 ID:zsy99021
      3 PROB:bzoj1004 
      4 LANG:C++
      5 */
      6 #include <cstdio>
      7 #include <cstring>
      8 #include <algorithm>
      9 #include <cmath>
     10 #include <iostream>
     11 #include <fstream>
     12 #include <ctime>
     13 #define N 68
     14 #define M 28
     15 #define mid(l,r) ((l+r) >> 1)
     16 #define INF 0x7ffffff
     17 using namespace std;
     18   
     19 int n,m,sr,sb,sg,rep[N],ans,f[N][M][M][M],loop[N],tot,reply,p;
     20   
     21 void init()
     22 {
     23     bool flag[N];
     24     for (int i = 1;i <= n;i++) scanf("%d",&rep[i]);
     25     memset(loop,0,sizeof(loop));
     26     memset(f,0,sizeof(f));
     27     f[0][0][0][0] = 1;
     28     memset(flag,0,sizeof(flag));
     29     tot = 0;
     30     reply = 0;
     31     for (int i = 1;i <= n;i++)
     32         if (!flag[i])
     33         {
     34             int r = 1;
     35             for (int j = rep[i];j != i;j = rep[j],r++) flag[j] = true;
     36             loop[++tot] = r;
     37         }
     38 }
     39   
     40 void init1()
     41 {
     42     bool flag[N];
     43     for (int i = 1;i <= n;i++) rep[i] = i;
     44     memset(loop,0,sizeof(loop));
     45     memset(f,0,sizeof(f));
     46     f[0][0][0][0] = 1;
     47     memset(flag,0,sizeof(flag));
     48     tot = 0;
     49     reply = 0;
     50     for (int i = 1;i <= n;i++)
     51         if (!flag[i])
     52         {
     53             int r = 1;
     54             for (int j = rep[i];j != i;j = rep[j],r++);
     55             loop[++tot] = r;
     56         }
     57 }
     58   
     59 void col(int x,int a,int b,int c)
     60 {
     61     if (a >= loop[x]) 
     62         f[x][a][b][c] += f[x-1][a-loop[x]][b][c];
     63     if (b >= loop[x]) 
     64         f[x][a][b][c] += f[x-1][a][b-loop[x]][c];
     65     if (c >= loop[x]) 
     66         f[x][a][b][c] += f[x-1][a][b][c-loop[x]];
     67     if (f[x][a][b][c] >= p) f[x][a][b][c] %= p;
     68 }
     69   
     70 int work()
     71 {
     72     init();
     73     for (int l = 1;l <= tot;l++)
     74         for (int i = 0;i <= sr;i++)
     75             for (int j = 0;j <= sb;j++)
     76                 for (int k = 0;k <= sg;k++)
     77                     col(l,i,j,k);
     78     return f[tot][sr][sb][sg];
     79 }
     80   
     81 int work1()
     82 {
     83     init1();
     84     for (int l = 1;l <= tot;l++)
     85         for (int i = 0;i <= sr;i++)
     86             for (int j = 0;j <= sb;j++)
     87                 for (int k = 0;k <= sg;k++)
     88                     col(l,i,j,k);
     89     return f[tot][sr][sb][sg];
     90 }
     91   
     92 int main()
     93 {
     94     scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&p);
     95     n = sr + sb + sg;
     96     for (int i = 1;i <= m;i++)
     97         ans += work();
     98     ans += work1();
     99     int c;
    100     for (int i = 1;;i++) 
    101         if ((i * (m + 1)) % p == 1)
    102         {
    103             c = i;
    104             break;
    105         }
    106     ans = ans * c % p;
    107     printf("%d
    ",ans);
    108     return 0;
    109 }
    View Code
  • 相关阅读:
    【总结】设备树语法及常用API函数【转】
    【转】ios蓝牙开发学习笔记(四)ios蓝牙应用的后台处理 -- 不错
    SQL存储过程相关信息查看
    DBCC CHECKIDENT
    SQL CAST与CONVERT区别
    SQl查询数据库库名,表名、表的列名
    SQL语句资料
    SQL中rownum和order by的执行顺序的问题
    完整的ASP.NET存储过程分页,排序,鼠标移至变色
    最简单删除SQL Server中所有数据的方法(不用考虑表之间的约束条件,即主表与子表的关系)
  • 原文地址:https://www.cnblogs.com/wulala979/p/3506144.html
Copyright © 2011-2022 走看看