D
e
s
c
r
i
p
t
i
o
n
\mathcal{Description}
Description
小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.
进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绿色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.
Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).
输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替, 且对每种洗牌法, 都存在一种洗牌法使得能回到原状态 .
S o l u t i o n \mathcal{Solution} Solution
加粗字体保证了多次洗牌与使用一次其中一种洗牌方式的结果相同,
相当于给出的置换总数仍是
M
M
M.
对于
M
M
M个置换, 根据
B
u
r
n
s
i
d
e
引
理
Burnside引理
Burnside引理,
我们只需将
M
M
M 个置换下 状态始终不变的方案数
n
u
m
i
num_i
numi 叠加,
再加上 题目没有给出的 单位置换 的方案数, 除
M
+
1
M+1
M+1 即可得到答案.
n
u
m
i
num_i
numi 可以使用
d
p
dp
dp 求解,
在第
i
i
i 个置换下, 含有
s
i
z
e
size
size个循环节, 每个循环节长度为
l
e
n
i
len_i
leni,
每个循环节内都必须染上同一个颜色才可以循环.
要求解的就是 三个不同的背包中 装 s i z e size size 个物品 .
设
F
[
i
,
j
,
k
]
F[i,j,k]
F[i,j,k] 表示三种颜色分别使用
i
,
j
,
k
i,j,k
i,j,k 个的方案数,
则
F
[
i
,
j
,
k
]
=
F
[
i
−
l
e
n
i
,
j
,
k
]
+
F
[
i
,
j
−
l
e
n
i
,
k
]
+
F
[
i
,
j
,
k
−
l
e
n
i
]
F[i,j,k]=F[i-len_i,j,k]+F[i,j-len_i,k]+F[i,j,k-len_i]
F[i,j,k]=F[i−leni,j,k]+F[i,j−leni,k]+F[i,j,k−leni],
最后
F
[
S
r
,
S
b
,
S
g
]
F[Sr,Sb,Sg]
F[Sr,Sb,Sg] 即为该置换方案数.
C
o
d
e
\mathcal{Code}
Code
#include<cstdio>
#include<cstring>
#define reg register
int Sr;
int Sb;
int Sg;
int M;
int N;
int P;
int mod;
int Ans;
int Len[75];
int len[75];
int Zh[75][75];
int F[75][75][75];
bool Used[75];
int gcd(int a, int b){ return !b?a:gcd(b, a%b); }
int Find_circle(int x){
memset(Used, 0, sizeof Used);
int s = 0;
for(reg int i = 1; i <= N; i ++)
if(!Used[i]){
Used[i] = 1;
len[++ s] = 1;
int to = Zh[x][i];
while(!Used[to]) Used[to] = 1, to = Zh[x][to], len[s] ++;
}
return s;
}
void Work(int size){
memset(F, 0, sizeof F);
F[0][0][0] = 1;
for(reg int p = 1; p <= size; p ++)
for(reg int i = Sr; ~i; i --)
for(reg int j = Sb; ~j; j --)
for(reg int k = Sg; ~k; k --){
int &t = F[i][j][k];
if(i >= len[p]) t = (1ll*t + F[i-len[p]][j][k]) % mod;
if(j >= len[p]) t = (1ll*t + F[i][j-len[p]][k]) % mod;
if(k >= len[p]) t = (1ll*t + F[i][j][k-len[p]]) % mod;
}
Ans = (1ll*Ans + F[Sr][Sb][Sg]);
}
int KSM(int a, int b){
a %= mod;
int s = 1;
while(b){
if(b & 1) s = 1ll*s*a % mod;
a = 1ll*a*a % mod;
b >>= 1;
}
return s;
}
int main(){
scanf("%d%d%d%d%d", &Sr, &Sb, &Sg, &M, &P);
mod = P;
N = Sr + Sb + Sg;
for(reg int i = 1; i <= M; i ++)
for(reg int j = 1; j <= N; j ++) scanf("%d", &Zh[i][j]);
for(reg int i = 1; i <= M; i ++) Work(Find_circle(i));
for(reg int i = 1; i <= N; i ++) len[i] = 1;
Work(N);
Ans = (1ll*Ans*KSM(M+1, mod-2)) % mod;
printf("%d\n", Ans);
return 0;
}