[HNOI2008]Cards

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3719  Solved: 2236

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。

思路

 根据群论里brunside引理,我们可以知道:

每一种洗牌方式,都是一个闭环,我们需要选择所有的单一操作下,循环节为1的方案的方案数之和,然后在除以方案数;

需要注意的是,不洗牌也是一种操作;

对于单一操作下,循环节为1的方案的方案数,我们可以通过背包DP求得;

因为ans需要取模,同时题目有除法操作,所以还需要求逆元;

对于样例的解释:

不操作:6,(2,3,1):0,(3,1,2):0;

ans=∑c()/(m+1)=6/3=2;

代码实现

 1 #include<cstdio>
 2 #include<cstring>
 3 const int maxn=62;
 4 int n,m,sr,sb,sg,mod,ans;
 5 int s[maxn];
 6 int v[maxn],size[maxn];
 7 int f[maxn][maxn][maxn];
 8 int get_c(){
 9     int cnt=0,p;
10     memset(v,0,sizeof(v));
11     memset(size,0,sizeof(size));
12     for(int i=1;i<=n;i++)
13     if(!v[i]){
14         p=s[i];
15         cnt++;
16         while(!v[p]) v[p]=1,size[cnt]++,p=s[p];
17     }
18     memset(f,0,sizeof(f));
19     f[0][0][0]=1;
20     for(int i=1,a=size[i];i<=cnt;i++,a=size[i])
21     for(int r=sr;r>=0;r--)
22     for(int b=sb;b>=0;b--)
23     for(int g=sg;g>=0;g--){
24         if(r>=a) f[r][b][g]+=f[r-a][b][g],f[r][b][g]%=mod;
25         if(b>=a) f[r][b][g]+=f[r][b-a][g],f[r][b][g]%=mod;
26         if(g>=a) f[r][b][g]+=f[r][b][g-a],f[r][b][g]%=mod;
27     }
28     return f[sr][sb][sg];
29 }
30 int ksm(int x,int k){
31     int ret=1;
32     while(k){
33         if(k&1) ret*=x,ret%=mod;
34         x*=x,x%=mod;
35         k>>=1;
36     }
37     return ret;
38 }
39 int main(){
40     scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&mod);
41     n=sr+sb+sg;
42     for(int i=1;i<=n;i++) s[i]=i;
43     ans+=get_c(),ans%=mod;
44     for(int i=1;i<=m;i++){
45         for(int j=1;j<=n;j++) scanf("%d",&s[j]);
46         ans+=get_c(),ans%=mod;
47     }
48     ans=(ans*ksm(m+1,mod-2))%mod;
49     printf("%d\n",ans);
50     return 0;
51 }

 

posted @ 2017-09-06 17:04  J_william  阅读(220)  评论(0编辑  收藏  举报