置换群,Burnside引理的应用.
注意置换群中一定有一个”不变”元素,计算时要记得考虑.
AC code:
#include <cstdio>
#include <vector>
using namespace std;
const int N=61;
int sr,sb,sg,m,p,n,ans;
int a[N];
int c[N][N];
void cal(){
int tot=0;
int L[N];
int f[N][21][21][21]={0};
bool h[N]={0};
for(int i=1;i<=n;i++){
if(h[a[i]]) continue;
int head=i,cnt=1;
h[head]=1;
for(int j=a[i];j!=head;h[j]=1,j=a[j],cnt++) ;
L[++tot]=cnt;
}
if(L[1]>sr||L[1]>sb||L[1]>sg) return ;
f[1][sr-L[1]][sb][sg]=f[1][sr][sb-L[1]][sg]=f[1][sr][sb][sg-L[1]]=1;
for(int i=2;i<=tot;i++){
for(int j=0;j<=sr-L[i];j++){
for(int k=0;k<=sb-L[i];k++){
for(int l=0;l<=sg-L[i];l++){
f[i][j][k][l]+=f[i-1][j+L[i]][k][l];
f[i][j][k][l]+=f[i-1][j][k+L[i]][l];
f[i][j][k][l]+=f[i-1][j][k][l+L[i]];
f[i][j][k][l]%=p;
}
}
}
}
ans=(ans+f[tot][0][0][0])%p;
}
int pow(int x,int y){
if(!y) return 1;
int z=pow(x,y>>1);
z=(z*z)%p;
if(y&1) z=(z*x)%p;
return z;
}
int main(){
scanf("%d%d%d%d%d",&sr,&sb,&sg,&m,&p);
n=sr+sb+sg;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++) scanf("%d",&a[j]);
cal();
}
c[1][0]=c[1][1]=1;
for(int i=2;i<=n;i++){
c[i][0]=1;
for(int j=1;j<=i+1;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%p;
}
ans=((ans+c[n][sr]*c[n-sr][sb])*pow(m+1,p-2))%p;
printf("%d\n",ans);
return 0;
}