BZOJ 1004-Cards(burnside引理)

来源:互联网 发布:雷蛇键盘mac驱动 编辑:程序博客网 时间:2024/06/05 09:58

1004: [HNOI2008]Cards

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3639  Solved: 2179
[Submit][Status][Discuss]

Description

  小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有
多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方
案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.
两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗
成另一种.Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).

Input

  第一行输入 5 个整数:Sr,Sb,Sg,m,p(m<=60,m+1<p<100)。n=Sr+Sb+Sg。
接下来 m 行,每行描述一种洗牌法,每行有 n 个用空格隔开的整数 X1X2...Xn,恰为 1 到 n 的一个排列,
表示使用这种洗牌法,第 i位变为原来的 Xi位的牌。输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代
替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。

Output

  不同染法除以P的余数

Sample Input

1 1 1 2 7
2 3 1
3 1 2

Sample Output

2

HINT

  有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG 

和GRB。

100%数据满足 Max{Sr,Sb,Sg}<=20。

Source


这里用到了burnside引理,不知道的可以自行百度:引理 ,包括置换群:置换群

补充一下群论的知识:

群的定义

  
是一个非空集合,
  
是它的一个二元运算,如果满足以下条件:
(1) 封闭性:若
  
,则存在唯一确定的
  
使得
  
(2) 结合律成立,即对
  
中任意元素
  
都有
  
(3) 单位元存在:存在
  
,对任意
  
,满足
  
  
称为单位元,也称幺元;
(4) 逆元存在:任意
  
,存在
  
  
  
为单位元),则称
  
  
互为逆元素,简称逆元。
  
记作
  
则称
  
  
构成一个群。
通常称
  
上的二元运算
  
为“乘法”,称
  
  
  
的积,并简写为
  
若群
  
中元素个数是有限的,则
  
称为有限群。否则称为无限群。有限群的元素个数称为有限群的阶。

定义运算

对于
  
,对于
  
的子集 
 
,定义
  
,简写为
  
  
,简写为
  
对于
  
的子集
  
,
  
,定义
  
,简写为
  
对于
  
的子集
  
,记
  

群的替换定理

  
是群,则对于任一
  
  

子群

  
是群,
  
  
的非空子集并且
  
也是群,那么称
  
  
的子群。
这条定理可以判定
  
的子集是否为一个子群:
 
  
 
 
  
的子群

由于题目中保证"任意多次洗牌都可用这mm种洗牌法中的一种代替",于是有了封闭性。

  结合律显然成立。

  题目中还保证了"对每种洗牌法,都存在一种洗牌法使得能回到原状态",逆元也有了。

  只剩下一个单位元,我们手动补上。单位元就是不洗牌。

  所以所有的洗牌方案构成了一个置换群。于是就可以用Burnside引理了。

 Burnside引理定义

编辑
设G={a1,a2,…ag}是目标集[1,n]上的置换群。每个置换都写成不相交循环的乘积。
  
是在置换ak的作用下不动点的个数,也就是长度为1的循环的个数。通过上述置换的变换操作后可以相等的元素属于同一个等价类。若G将[1,n]划分成l个等价类,则:
等价类个数为:
 

根据BurnsideBurnside引理,本质不同的染色数目ansans就是C(f)C(f)的平均数。于是我们可以暴力算出C(f)C(f),由于是在模意义下,所以除法变为逆元。

  当然,这里的暴力方法不是指指数级的枚举,而是dpdp。因为一种方案要在一个置换下本质不变,那么在同一个循环内的位置颜色必定相等。于是把所有循环都抠出来然后暴力三维背包就可以了。

要注意的一点是总的洗牌方案是m+1,而不是m,因为有单位元的存在。。。。


#include<map>#include<stack>#include<queue>#include<vector>#include<math.h>#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>using namespace std;typedef long long  ll;#define  maxn 77int a[maxn],flag[maxn],b[maxn],p,n,m,dp[maxn][maxn][maxn],sr,sb,sg;int pows(int x,int y){int res=1;while(y){if(y%2)res=res*x%p;x=x*x%p;y/=2;}return res;}int work(){int i,j,cnt=0,k,r;memset(dp,0,sizeof(dp));memset(flag,0,sizeof(flag));for(i=1;i<=n;i++){if(flag[i]==0){b[++cnt]=0;for(j=i;flag[j]==0;j=a[j]){flag[j]=1;b[cnt]++;}}}dp[0][0][0]=1;for(i=1;i<=cnt;i++)for(j=sr;j>=0;j--)for(k=sb;k>=0;k--)for(r=sg;r>=0;r--){if(j>=b[i])dp[j][k][r]+=dp[j-b[i]][k][r];if(k>=b[i])dp[j][k][r]+=dp[j][k-b[i]][r];if(r>=b[i])dp[j][k][r]+=dp[j][k][r-b[i]];dp[j][k][r]%=p;} return dp[sr][sb][sg];}int  main(void){int i,j,ans=0;scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&p);n=sr+sb+sg;for(i=1;i<=n;i++)a[i]=i;ans+=work();for(i=1;i<=m;i++){for(j=1;j<=n;j++)scanf("%d",&a[j]);ans=(ans+work())%p;}ans=(ans*pows(m+1,p-2))%p;printf("%d\n",ans);return 0;}


原创粉丝点击