【HNOI2008】Cards BZOJ 1004

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。

思路

     嘛~首先可以看到是组合数学的题目。之后就想大概是个什么模型。

     看到在一个排列中元素可以交换,就想到了置换群,Polya计数法。

     那么就是有这么多给你的置换方法,求本质上不同的排列有多少。我们将每一个置换群分解,找出置换的循环节:

     比如置换1 4 3 5 2可以分解为(1)(2 4 5)(3)这三个循环节。

     由polya计数法得:ans=所有置换中本质相同的方案的平均数。

     看到100%数据满足 Max{Sr,Sb,Sg}<=20可以用dp做。

     用f[i][j][k]表示用了i个红色,j个绿色,k个蓝色的方案数,则f[i][j][k]=f[i-size][j][k]+f[i][j-size][k]+f[i][j][k-size](size是当前循环节的大小)

     最后不要忘记加上(1 2 3 ....n)这个循环,这个可以直接算出有N!/(sr!*sg!*sb!)

     然后除以m+1,p为质数,用逆元算。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <queue>
 9 #include <stack>
10 #include <map>
11 #include <set>
12 #include <list>
13 #include <vector>
14 #include <ctime>
15 #include <functional>
16 #define pritnf printf
17 #define scafn scanf
18 #define For(i,j,k) for(int i=(j);i<=(k);(i)++)
19 #define Clear(a) memset(a,0,sizeof(a))
20 using namespace std;
21 typedef long long LL;
22 typedef unsigned int Uint; 
23 const int INF=0x7fffffff;
24 //==============struct declaration==============
25 
26 //==============var declaration=================
27 const int MAXN=30;
28 int sr,sb,sg,n,m,p,MOD,cnt=0;
29 int ans=0;
30 int tran[MAXN*5];
31 int f[MAXN][MAXN][MAXN];
32 bool vis[MAXN*5];
33 //==============function declaration============
34 int quickpow(int a,int Exp);
35 int inv(int a){return quickpow(a,MOD-2);}
36 int fact(int x){int ans=1;for(int i=1;i<=x;i++) ans=(ans*i)%MOD;return ans;}
37 //==============main code=======================
38 int main()
39 {  
40 //#define FILE__
41 #ifdef FILE__
42   freopen("input.txt","r",stdin);
43   freopen("output.txt","w",stdout);
44 #endif  
45   scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&MOD);n=sr+sb+sg;
46   for(int i=1;i<=m;i++){
47     for(int j=1;j<=n;j++)
48       scanf("%d",tran+j);
49     memset(vis,false,sizeof(vis));memset(f,0,sizeof(f));
50     f[0][0][0]=1;cnt=0;
51     for(int p=1;p<=n;p++){
52       if (vis[p]) continue;
53       cnt++;
54       int siz=0,x=p;
55       while (!vis[x]){
56         vis[x]=true;
57         x=tran[x];
58         siz++;
59       }//找出循环节
60       for(int i=sr;i>=0;i--)
61         for(int j=sb;j>=0;j--)
62           for(int k=sg;k>=0;k--){
63             if (i>=siz) f[i][j][k]+=f[i-siz][j][k];
64             if (j>=siz) f[i][j][k]+=f[i][j-siz][k];
65             if (k>=siz) f[i][j][k]+-f[i][j][k-siz];
66             f[i][j][k]%=MOD;
67           } //DP
68     }
69     ans=(ans+f[sr][sb][sg])%MOD;
70   }
71   ans=ans+fact(n)*inv(fact(sr))*inv(fact(sb))*inv(fact(sg));
72   printf("%d\n",(ans*inv(m+1))%MOD);
73   return 0;
74 }
75 //================fuction code====================
76 int quickpow(int a,int Exp){
77   if (Exp==0) return 1;
78   if (Exp==1) return a;
79   int temp=quickpow(a,Exp/2);
80   temp=(temp*temp)%MOD;
81   if (Exp&1) temp=(temp*a)%MOD;
82   return temp;
83 }
Code

 

posted @ 2015-01-11 22:05  Houjikan  阅读(126)  评论(0编辑  收藏  举报