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种洗牌法中的一种代替,且对每种
洗牌法,都存在一种洗牌法使得能回到原状态。
Output
不同染法除以P的余数
Sample Input
1 1 1 2 7
2 3 1
3 1 2
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。
Burnside定理大法好,对于任一置换,用背包DP求出方案数
P.S.别忘了添加不改变的置换
1 #include <cstdio> 2 #include <cstring> 3 int Sr, Sb, Sg, n, m, p, a[100][100]; 4 5 int pow_mod(int a, int k, int m) { 6 if (k == 0) return 1; 7 if (k == 1) return a % m; 8 int ret = pow_mod(a, k / 2, m); 9 ret = ret * ret % m; 10 if (k % 2 == 1) ret = ret * a % m; 11 return ret; 12 } 13 14 int inv(int a, int m) { 15 return pow_mod(a, m - 2, m); 16 } 17 18 void init() { 19 scanf("%d%d%d%d%d", &Sr, &Sb, &Sg, &m, &p); 20 n = Sr + Sb + Sg; 21 for (int i = 1; i <= m; i++) 22 for (int j = 1; j <= n; j++) scanf("%d", &a[i][j]); 23 m++; 24 for (int i = 1; i <= n; i++) a[m][i] = i; 25 } 26 27 int compute(int t) { 28 int N = 0, arr[100], f[50][50][50]; 29 bool b[100]; memset(b, 0, sizeof(b)); 30 for (int i = 1; i <= n; i++) 31 if (!b[i]) { 32 int pos = i, cnt = 0; 33 while (!b[pos]) { 34 b[pos] = 1; cnt++; 35 pos = a[t][pos]; 36 } 37 arr[++N] = cnt; 38 } 39 memset(f, 0, sizeof(f)); 40 int sum = 0; 41 f[0][0][0] = 1; 42 for (int i = 1; i <= N; i++) { 43 sum += arr[i]; 44 for (int i1 = Sr; i1 >= 0; i1--) 45 for (int i2 = Sb; i2 >= 0; i2--) 46 for (int i3 = Sg; i3 >= 0; i3--) 47 if (i1 + i2 + i3 == sum) { 48 if (i1 >= arr[i]) 49 f[i1][i2][i3] = (f[i1][i2][i3] + f[i1 - arr[i]][i2][i3]) % p; 50 if (i2 >= arr[i]) 51 f[i1][i2][i3] = (f[i1][i2][i3] + f[i1][i2 - arr[i]][i3]) % p; 52 if (i3 >= arr[i]) 53 f[i1][i2][i3] = (f[i1][i2][i3] + f[i1][i2][i3 - arr[i]]) % p; 54 } 55 } 56 return f[Sr][Sb][Sg]; 57 } 58 59 int main() { 60 //freopen("input.txt", "r", stdin); 61 //freopen("output.txt", "w", stdout); 62 init(); 63 int ans = 0; 64 for (int i = 1; i <= m; i++) 65 ans = (ans + compute(i)) % p; 66 ans = (ans * inv(m, p)) % p; 67 printf("%d\n", ans); 68 return 0; 69 }