【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

 

 

你可能感兴趣的:(2008)