BZOJ1004: [HNOI2008]Cards

1004: [HNOI2008]Cards

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3938  Solved: 2377
[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

 

【题解】

等价于给定一个置换集F(需要自己加入(1,2,3,4,...,n)),用三种颜色且每种颜色指定个数进行染色的方案数

使用Burnside定理,求不动点个数

不难发现,不动点就是循环节为1的循环,求出每个置换f的循环后,在循环上染色,使得每个独立的循环颜色相等,能够独立出来变成循环节为1的循环

这个可以用多重背包做,不需要任何优化,体积就是原循环节大小,往三个背包(三种颜色)里装,装满的方案数

做完之后,把所有不动点个数相加,除以置换数m + 1即可

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #include <algorithm>
 6 #include <queue>
 7 #include <vector>
 8 #include <cmath> 
 9 #define min(a, b) ((a) < (b) ? (a) : (b))
10 #define max(a, b) ((a) > (b) ? (a) : (b))
11 #define abs(a) ((a) < 0 ? (-1 * (a)) : (a))
12 inline void swap(int &a, int &b)
13 {
14     long long tmp = a;a = b;b = tmp;
15 }
16 inline void read(int &x)
17 {
18     x = 0;char ch = getchar(), c = ch;
19     while(ch < '0' || ch > '9') c = ch, ch = getchar();
20     while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
21     if(c == '-') x = -x;
22 }
23 
24 const int INF = 0x3f3f3f3f;
25 const int MAXN = 60;
26 
27 int dp[MAXN + 5][MAXN + 5][MAXN + 5], size[MAXN], tot, s[MAXN], b[MAXN];
28 int sr,sb,sg,m,p,n,sum;
29 
30 int pow(int a, int b)
31 {
32     int r = 1, base = a;
33     for(;b;b >>= 1)
34     {
35         if(b & 1) r *= base, r %= p;
36         base *= base, base %= p;
37     }
38     return r;
39 } 
40 
41 int ni(int x)
42 {
43     return pow(x, p - 2);
44 }
45 
46 int main()
47 {
48     read(sr), read(sb), read(sg), read(m), read(p);
49     n = sr + sb + sg;
50     for(register int i = 1;i <= m + 1;++ i)
51     {            
52         tot = 0;memset(dp, 0, sizeof(dp));memset(size, 0, sizeof(size));memset(b, 0, sizeof(b));    
53         if(i == m + 1) for(register int j = 1;j <= n;++ j) s[j] = j;
54         else for(register int j = 1;j <= n;++ j) read(s[j]);
55         for(register int j = 1;j <= n;++ j)
56             if(!b[j])
57             {
58                 ++ tot;
59                 int k = j;
60                 do
61                 {
62                     b[k] = 1;
63                         ++ size[tot];
64                     k = s[k];
65                 }while(k != j);
66             }
67         dp[0][0][0] = 1;
68         for(register int j = 1;j <= tot;++ j)
69             for(register int r = sr;r >= 0;-- r)
70                 for(register int b = sb;b >= 0;-- b)
71                     for(register int g = sg;g >= 0;-- g)
72                     {
73                         int &tmp = dp[r][b][g];
74                         if(g >= size[j]) tmp += dp[r][b][g - size[j]];if(tmp >= p) tmp -= p;
75                         if(b >= size[j]) tmp += dp[r][b - size[j]][g];if(tmp >= p) tmp -= p;
76                         if(r >= size[j]) tmp += dp[r - size[j]][b][g];if(tmp >= p) tmp -= p;
77                     }
78         sum += dp[sr][sb][sg];
79         if(sum >= p) sum -= p;
80     }
81     printf("%d\n", sum * ni(m + 1) % p);
82     return 0;
83 }
BZOJ1004

 

posted @ 2018-01-19 21:58  嘒彼小星  阅读(148)  评论(0编辑  收藏  举报