bzoj1004 [HNOI2008]Cards 置换群+背包

【bzoj1004】[HNOI2008]Cards

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种洗牌法中的一种代替,且对每种
洗牌法,都存在一种洗牌法使得能回到原状态。

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

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。

题解

 

  置换的循环在不变元素中一定是一个颜色,所以只需要dp一下这个是属于那种颜色的

  就是换了一种求不动点的方式

  然后可以求一个三维的01背包的方案数。而最后的

  除法需要利用扩展欧几里得求乘法的逆元。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 inline int read()
 6 {
 7     int x=0;char ch=getchar();
 8     while(ch<'0'||ch>'9')ch=getchar();
 9     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
10     return x;
11 }
12 int s1,s2,s3,n,m,mod,ans;
13 int a[70][70],f[70][70][70],d[70];
14 bool b[70];
15 int dp(int x)
16 {
17     for(int i=1;i<=n;i++)b[i]=0;
18     int sum=0,p;
19     for(int i=1;i<=n;i++)
20        if(!b[i])
21        {
22            d[++sum]=1;p=i;
23            b[p]=1;
24            while(!b[a[x][p]])
25            {
26                d[sum]++;
27                b[a[x][p]]=1;
28                p=a[x][p];
29            }
30        }
31     for(int i=s1;i>=0;i--)
32         for(int j=s2;j>=0;j--)
33             for(int k=s3;k>=0;k--)
34                 f[i][j][k]=0;
35     f[0][0][0]=1;
36     for(int h=1;h<=sum;h++)
37         for(int i=s1;i>=0;i--)
38             for(int j=s2;j>=0;j--)
39                 for(int k=s3;k>=0;k--)
40                 {
41                     if(i>=d[h])f[i][j][k]=(f[i][j][k]+f[i-d[h]][j][k])%mod;
42                     if(j>=d[h])f[i][j][k]=(f[i][j][k]+f[i][j-d[h]][k])%mod;
43                     if(k>=d[h])f[i][j][k]=(f[i][j][k]+f[i][j][k-d[h]])%mod;
44                 }
45     return f[s1][s2][s3];
46 }
47 void exgcd(int a,int b,int &x,int &y)
48 {
49      if(b==0){x=1;y=0;return;}
50      exgcd(b,a%b,x,y);
51      int t=x;x=y;y=t-a/b*y;
52 }
53 int main()
54 {
55     s1=read(),s2=read(),s3=read(),m=read(),mod=read();
56     n=s1+s2+s3;
57     for(int i=1;i<=m;i++)
58         for(int j=1;j<=n;j++)
59             a[i][j]=read();
60     m++;
61     for(int i=1;i<=n;i++)a[m][i]=i;
62     for(int i=1;i<=m;i++)
63         ans=(ans+dp(i))%mod;
64     int x,y;
65     exgcd(m,mod,x,y);
66     while(x<=0)x+=mod,y-=m;
67     printf("%d",ans*x%mod);
68     return 0;
69 }

 

posted @ 2017-10-26 07:12  Kaiser-  阅读(137)  评论(0编辑  收藏  举报