BZOJ1004: [HNOI2008]Cards
2018-01-19 21:58
197 查看
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
1 1 1 2 72 3 1
3 1 2
Sample Output
2HINT
有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即可
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <queue> #include <vector> #include <cmath> #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) #define abs(a) ((a) < 0 ? (-1 * (a)) : (a)) inline void swap(int &a, int &b) { long long tmp = a;a = b;b = tmp; } inline void read(int &x) { x = 0;char ch = getchar(), c = ch; while(ch < '0' || ch > '9') c = ch, ch = getchar(); while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar(); if(c == '-') x = -x; } const int INF = 0x3f3f3f3f; const int MAXN = 60; int dp[MAXN + 5][MAXN + 5][MAXN + 5], size[MAXN], tot, s[MAXN], b[MAXN]; int sr,sb,sg,m,p,n,sum; int pow(int a, int b) { int r = 1, base = a; for(;b;b >>= 1) { if(b & 1) r *= base, r %= p; base *= base, base %= p; } return r; } int ni(int x) { return pow(x, p - 2); } int main() { read(sr), read(sb), read(sg), read(m), read(p); n = sr + sb + sg; for(register int i = 1;i <= m + 1;++ i) { tot = 0;memset(dp, 0, sizeof(dp));memset(size, 0, sizeof(size));memset(b, 0, sizeof(b)); if(i == m + 1) for(register int j = 1;j <= n;++ j) s[j] = j; else for(register int j = 1;j <= n;++ j) read(s[j]); for(register int j = 1;j <= n;++ j) if(!b[j]) { ++ tot; int k = j; do { b[k] = 1; ++ size[tot]; k = s[k]; }while(k != j); } dp[0][0][0] = 1; for(register int j = 1;j <= tot;++ j) for(register int r = sr;r >= 0;-- r) for(register int b = sb;b >= 0;-- b) for(register int g = sg;g >= 0;-- g) { int &tmp = dp[r][b][g]; if(g >= size[j]) tmp += dp[r][b][g - size[j]];if(tmp >= p) tmp -= p; if(b >= size[j]) tmp += dp[r][b - size[j]][g];if(tmp >= p) tmp -= p; if(r >= size[j]) tmp += dp[r - size[j]][b][g];if(tmp >= p) tmp -= p; } sum += dp[sr][sb][sg]; if(sum >= p) sum -= p; } printf("%d\n", sum * ni(m + 1) % p); return 0; }
BZOJ1004
相关文章推荐
- bzoj1004:[HNOI2008]Cards
- BZOJ1004【HNOI2008】Cards
- BZOJ1004:[HNOI2008]Cards (Burnside引理+DP+Exgcd)
- bzoj1004: [HNOI2008]Cards
- bzoj1004 [HNOI2008]Cards
- bzoj1004: [HNOI2008]Cards [Burnside&Ploya+求逆元]
- bzoj1004 [HNOI2008]Cards
- [HNOI2008]Cards
- 洛谷P1446 [HNOI2008]Cards
- Bzoj1004 [HNOI2008]Cards
- BZOJ1004: [HNOI2008]Cards
- [bzoj1004][HNOI2008]Cards【群论】
- [BZOJ1004] [HNOI2008]Cards解题报告(Burnside引理)
- 置换群burnside引理(bzoj 1004: [HNOI2008]Cards)
- bzoj1004【hnoi2008】Cards
- 【数论】【动态规划】[BZOJ1004][HNOI2008]Cards
- [BZOJ1004][HNOI2008]Cards(Burnside引理+DP)
- BZOJ 1004 [HNOI2008]Cards
- [BZOJ1004]HNOI2008 Cards |polya|置换群|DP|乘法逆元
- 【BZOJ】【1004】【HNOI2008】Cards