1004: [HNOI2008]Cards
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 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
2 3 1
3 1 2
Sample Output
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 }