burnside引理:ans=sum(x(f)=置换f的不动点数)/(|G|=置换数);
置换f的不动点:如果方案x经过置换f后仍然不变,它就是置换f的一个不动点。
polya计数:将置换分解成轮换后直接推不动点数。
以本题为例,分解成轮换后,不动点满足:1.每个轮换内部所有点颜色相同。2.对于整个置换而言,sum1=a,sum2=b,sum3=c。
dp即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline int read(){
int x=0; int w=0; char ch=0;
while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return w? -x:x;
}
LL P;
void ex_gcd(LL a,LL b,LL c,LL &x,LL &y){
if (!a){ x=0; y=c/b; return; }
if (!b){ y=0; x=c/a; return; }
ex_gcd(b,a%b,c,y,x);
y-=(a/b)*x;
}
LL inv(LL t){
t%=P; LL x,y; ex_gcd(t,P,1,x,y); return (x%P);
}
const int N=65;
const int A=25;
int nedge,hed[N],vis[N],d[N],f[N][A][A][A];
struct Edge{ int to,nex; }edge[N<<1];
void addedge(int a,int b){
edge[nedge].to=b; edge[nedge].nex=hed[a]; hed[a]=nedge++;
}
int dfs(int k){
int res=1; vis[k]=1;
repedge(i,k){ int v=edge[i].to;
if (!vis[v]) res+=dfs(v); }
return res;
}
int a,b,c,m;
int main(){
a=read(); b=read(); c=read(); m=read(); P=read(); int n=a+b+c; LL ans=0;
rep(i,0,m){
int tot=0; if (i){ nedge=0; rep(j,1,n) hed[j]=-1; rep(j,1,n) { int x=read(); addedge(x,j); }
rep(j,1,n) vis[j]=0; tot=0; rep(j,1,n) if (!vis[j]) d[++tot]=dfs(j); }
else{ tot=n; rep(j,1,n) d[j]=1; }
rep(j,0,tot-1) rep(t1,0,a) rep(t2,0,b) rep(t3,0,c) f[j][t1][t2][t3]=0; f[0][0][0][0]=1;
rep(j,0,tot-1) rep(t1,0,a) rep(t2,0,b) rep(t3,0,c){
if (t1+d[j+1]<=a) f[j+1][t1+d[j+1]][t2][t3]=(f[j+1][t1+d[j+1]][t2][t3]+f[j][t1][t2][t3])%P;
if (t2+d[j+1]<=b) f[j+1][t1][t2+d[j+1]][t3]=(f[j+1][t1][t2+d[j+1]][t3]+f[j][t1][t2][t3])%P;
if (t3+d[j+1]<=c) f[j+1][t1][t2][t3+d[j+1]]=(f[j+1][t1][t2][t3+d[j+1]]+f[j][t1][t2][t3])%P;
}
ans=(ans+f[tot][a][b][c])%P;
}
ans=(ans*inv(m+1))%P; ans=(ans%P+P)%P;
printf("%lld\n",ans); return 0;
}