小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.
进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绿色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.
Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).
输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替, 且对每种洗牌法, 都存在一种洗牌法使得能回到原状态 .
加粗字体保证了多次洗牌与使用一次其中一种洗牌方式的结果相同,
相当于给出的置换总数仍是 .
对于个置换, 根据 ,
我们只需将 个置换下 状态始终不变的方案数 叠加,
再加上 题目没有给出的 单位置换 的方案数, 除 即可得到答案.
可以使用 求解,
在第 个置换下, 含有个循环节, 每个循环节长度为 ,
每个循环节内都必须染上同一个颜色才可以循环.
要求解的就是 三个不同的背包中 装 个物品 .
设 表示三种颜色分别使用 个的方案数,
则 ,
最后 即为该置换方案数.
#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
", Ans);
return 0;
}