$ color{#0066ff}{ 题目描述 }$
小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.
进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绿色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.
Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).
(color{#0066ff}{输入格式})
第一行输入 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种洗牌法中的一种代替,且对每种
洗牌法,都存在一种洗牌法使得能回到原状态。
100%数据满足 Max{Sr,Sb,Sg}<=20。
(color{#0066ff}{输出格式})
不同染法除以P的余数
(color{#0066ff}{输入样例})
1 1 1 2 7
2 3 1
3 1 2
(color{#0066ff}{输出样例})
2
(color{#0066ff}{数据范围与提示})
有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG 和GRB。
[HNOI2008]
(color{#0066ff}{题解})
因为有颜色数量的限制,所以Polya就不太可行了
没事,我们还有Burnside
于是我们要找每个置换下不变的方案数
不难发现,对于每个循环,循环中的颜色必须相同才行
于是把当前置换的循环处理出来,当成物品跑01背包,就可以求出不变的方案数
注意还有额外的一种置换是不洗牌,也就是说不变,要特殊处理
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
int f[50][50][50];
bool vis[100];
int mp[100];
int siz[100];
int ans;
int a, b, c, m, mod, n;
LL ksm(LL x, LL y) {
LL re = 1LL;
while(y) {
if(y & 1) re = re * x % mod;
x = x * x % mod;
y >>= 1;
}
return re;
}
int work(int flag) {
memset(f, 0, sizeof f);
for(int i = 1; i <= n; i++) vis[i] = 0, siz[i] = 0;
int num = 0;
if(!flag) {
for(int i = 1; i <= n; i++) {
if(vis[i]) continue;
num++;
int now = i;
while(!vis[now]) vis[now] = true, siz[num]++, now = mp[now];
}
}
else {
num = n;
for(int i = 1; i <= n; i++) siz[i] = 1;
}
f[0][0][0] = 1;
for(int i = 1; i <= num; i++)
for(int A = a; A >= 0; A--)
for(int B = b; B >= 0; B--)
for(int C = c; C >= 0; C--) {
if(A >= siz[i]) (f[A][B][C] += f[A - siz[i]][B][C]) %= mod;
if(B >= siz[i]) (f[A][B][C] += f[A][B - siz[i]][C]) %= mod;
if(C >= siz[i]) (f[A][B][C] += f[A][B][C - siz[i]]) %= mod;
}
return f[a][b][c];
}
int main() {
n = (a = in()) + (b = in()) + (c = in());
m = in(), mod = in();
for(int i = 1; i <= m; i++) {
for(int j = 1; j <= n; j++) mp[j] = in();
(ans += work(0)) %= mod;
}
(ans += work(1)) %= mod;
printf("%lld", 1LL * ans * ksm(m + 1, mod - 2) % mod);
return 0;
}