【题目链接】
【思路要点】
- 题意表明,给定的洗牌方式在加上单位置换后(原本存在就不需要再加了)是一个置换群。考虑Burnside引理或Polya定理。
- 注意到题目有颜色的使用次数的限制,因此不便使用Polya定理,考虑使用Burnside引理,求解每一个置换的本质不同的染色方案数的平均数。
- 显然可以设计动态规划在\(O(N*R*G*B)\)的时空复杂度内求解一个置换的本质不同的染色方案数。
- 因此,总时间复杂度为\(O(N*M*R*G*B)\),可以通过本题。
【代码】
#include<bits/stdc++.h> using namespace std; #define MAXN 65 #define MAXM 65 #define MAXC 25 int R, G, B, m, P, n, cnt, now; int f[MAXM][MAXN]; bool visited[MAXN]; bool mark[MAXN][MAXC][MAXC][MAXC]; int value[MAXN][MAXC][MAXC][MAXC]; void visit(int pos) { if (visited[pos]) return; cnt++; visited[pos] = true; visit(f[now][pos]); } void clean(int pos) { if (!visited[pos]) return; visited[pos] = false; clean(f[now][pos]); } int get(int pos, int R, int G, int B) { if (pos == 0) return 1; if (visited[pos]) return get(pos - 1, R, G, B); if (mark[pos][R][G][B]) return value[pos][R][G][B]; cnt = 0; value[pos][R][G][B] = 0; visit(pos); int tmp = cnt; if (R >= tmp) value[pos][R][G][B] += get(pos - 1, R - tmp, G, B); if (G >= tmp) value[pos][R][G][B] += get(pos - 1, R, G - tmp, B); if (B >= tmp) value[pos][R][G][B] += get(pos - 1, R, G, B - tmp); clean(pos); value[pos][R][G][B] %= P; mark[pos][R][G][B] = true; return value[pos][R][G][B]; } int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return tmp * tmp % P; else return tmp * tmp % P * x % P; } int main() { scanf("%d%d%d%d%d", &R, &G, &B, &m, &P); n = R + G + B; bool flg = false; for (int i = 1; i <= m; i++) { bool now = true; for (int j = 1; j <= n; j++) { scanf("%d", &f[i][j]); now &= f[i][j] == j; } flg |= now; } if (!flg) { m++; for (int i = 1; i <= n; i++) f[m][i] = i; } int ans = 0; for (int i = 1; i <= m; i++) { now = i; memset(mark, false, sizeof(mark)); ans += get(n, R, G, B); ans %= P; } printf("%d\n", ans * power(m, P - 2) % P); return 0; }