BZOJ 1004 [HNOI2008]Cards
因为那篇博客更不动了。。编辑一下要卡顿好久。。
还是一个题一个题更吧。。
一看到洗牌之后会等价这种就差不多是等价类计数,要用Burnside或者polya来计算。
看了好久才有点懂这部分究竟咋做。
首先要满足是一个置换群,那么就得补上单位元。
因为有颜色限制,所以不能polya,只能用Burnside。
Burnside的核心就是求出每一个置换有多少方案是不动的。
那么就是把每种置换独立开来,置换里的每个循环看成一个循环长度大小的盒子,然后用那么多颜色来染色。
可以用三维背包做,也可以用组合计数做。(如果可以,就不需要背包了。。。)
这里用了背包。
#include <bits/stdc++.h> const int N = 67; int dp[N][N][N], trans[N][N], sr, sb, sg, MOD, m, n; int a[N]; bool vis[N]; void M(int &a) { if (a >= MOD) a -= MOD; if (a < 0) a += MOD; } int solve(int index) { memset(a, 0, sizeof(a)); memset(vis, 0, sizeof(vis)); memset(dp, 0, sizeof(dp)); dp[0][0][0] = 1; int cnt = 0; for (int i = 1; i <= n; i++) { if (vis[i]) continue; ++cnt; int p = i; while (!vis[p]) { vis[p] = 1; a[cnt]++; p = trans[index][p]; } } for (int i = 1; i <= cnt; i++) for (int j = sr; j >= 0; j--) for (int k = sb; k >= 0; k--) for (int l = sg; l >= 0; l--) { if (j >= a[i]) M(dp[j][k][l] += dp[j - a[i]][k][l]); if (k >= a[i]) M(dp[j][k][l] += dp[j][k - a[i]][l]); if (l >= a[i]) M(dp[j][k][l] += dp[j][k][l - a[i]]); } return dp[sr][sb][sg]; } int qp(int a, int b = MOD - 2) { int ans = 1; while (b) { if (b & 1) ans = 1LL * ans * a % MOD; a = 1LL * a * a % MOD; b >>= 1; } return ans % MOD; } int main() { scanf("%d%d%d%d%d", &sr, &sb, &sg, &m, &MOD); n = sr + sb + sg; for (int i = 0; i < m; i++) for (int j = 1; j <= n; j++) scanf("%d", trans[i] + j); for (int j = 1; j <= n; j++) trans[m][j] = j; int ans = 0; for (int i = 0; i <= m; i++) M(ans += solve(i)); ans = 1LL * ans * qp(m + 1) % MOD; printf("%d\n", ans); return 0; }