BZOJ传送门
洛谷传送门
解析:
首先题目告诉我们给的置换就是一个群,那么可以直接上Burnside。
发现这个染色有个数限制,不能用Polya。
那么怎么求置换不动点个数?
显然我们只要求一个循环里面的所有点染上相同的颜色,把所有循环以及循环的长度求出来,然后做背包方案数就行了。
由于懒得写DP就直接上记搜了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc getchar
#define cs const
namespace IO{
template<typename T>
inline T get(){
re char c;
while(!isdigit(c=gc()));re T num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int getint(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
int mod;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Inc(int &a,int b){(a+=b)>=mod?a-=mod:a;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int quickpow(int a,int b,int res=1){
while(b){
if(b&1)res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
int f[65][25][25][25];
int vis[65][25][25][25],idx=-1;
int siz[65],a[65];
bool cur[65];
inline int dp(int now,int a,int b,int c){
if(!now)return 1;
if(vis[now][a][b][c]==idx)return f[now][a][b][c];
vis[now][a][b][c]=idx;
int &res=f[now][a][b][c];
res=0;
if(a>=siz[now])Inc(res,dp(now-1,a-siz[now],b,c));
if(b>=siz[now])Inc(res,dp(now-1,a,b-siz[now],c));
if(c>=siz[now])Inc(res,dp(now-1,a,b,c-siz[now]));
return res;
}
int sa,sb,sc,m,p,n,ans;
signed main(){
sa=getint(),sb=getint(),sc=getint(),m=getint(),mod=getint();
n=sa+sb+sc;
for(int re i=1;i<=n;++i)siz[i]=1;
ans=dp(n,sa,sb,sc);
for(idx=1;idx<=m;++idx){
memset(cur+1,0,sizeof(bool)*n);
for(int re i=1;i<=n;++i)a[i]=getint();
int cnt=0;
for(int re i=1;i<=n;++i)if(!cur[i]){
siz[++cnt]=0;
for(int re j=i;!cur[j];j=a[j])cur[j]=true,++siz[cnt];
}
Inc(ans,dp(cnt,sa,sb,sc));
}
cout<<quickpow(m+1,mod-2,ans)<<"\n";
return 0;
}