题目
思路
polya显然不可做,原因是本题有颜色限制。
使用burnside引理,用dp解决某个置换下的结果并求和。
然后费马小定理求m+1的逆元,和上面的结果相乘。
m+1是m个给定置换加一个恒等置换。
dp设
f
a
,
b
,
c
f_{a,b,c}
fa,b,c为a个红,b个蓝,c个绿的结果。
显然有
f
0
,
0
,
0
=
1
f_{0,0,0}=1
f0,0,0=1
又有
f
a
,
b
,
c
=
f
a
−
第
i
个
轮
换
长
度
,
b
,
c
+
f
a
,
b
−
第
i
个
轮
换
长
度
,
c
+
f
a
,
b
,
c
−
第
i
个
轮
换
长
度
(
做
得
到
的
情
况
下
)
f_{a,b,c}=f_{a-第i个轮换长度,b,c}+f_{a,b-第i个轮换长度,c}+f_{a,b,c-第i个轮换长度}(做得到的情况下)
fa,b,c=fa−第i个轮换长度,b,c+fa,b−第i个轮换长度,c+fa,b,c−第i个轮换长度(做得到的情况下)
code:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
//When I wrote this code,God and I unterstood what was I doing
inline int read()
{
int ret,c,f=1;
while (((c=getchar())> '9'||c< '0')&&c!='-');
if (c=='-') f=-1,ret=0;
else ret=c-'0';
while ((c=getchar())>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+(c^48);
return ret*f;
}
int n,m,tot,f[21][21][21],sr,sb,sg,p,zh[61],ans,le[61];
bool vis[101];
int kyx()
{
memset(vis,0,sizeof(vis));
memset(f,0,sizeof(f));
tot=0;
for (int i=1;i<=n;i++)
{
if (vis[i]) continue;
int len=0;
while (!vis[i])
{
vis[i]=1;
i=zh[i];
len++;
}
le[tot++]=len;
}
f[0][0][0]=1;
for (int i=0;i<tot;i++)
{
for (int j=sr;j>=0;j--)
{
for (int k=sb;k>=0;k--)
{
for (int u=sg;u>=0;u--)
{
if (j>=le[i])
{
f[j][k][u]=(f[j][k][u]+f[j-le[i]][k][u])%p;
}
if (k>=le[i])
{
f[j][k][u]=(f[j][k][u]+f[j][k-le[i]][u])%p;
}
if (u>=le[i])
{
f[j][k][u]=(f[j][k][u]+f[j][k][u-le[i]])%p;
}
}
}
}
}
return f[sr][sb][sg];
}
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y&1) ans=ans*x%p;
x=x*x%p;
y>>=1;
}
return ans;
}
int main()
{
sr=read(),sb=read(),sg=read(),m=read(),p=read();
n=sr+sb+sg;
for (int i=1;i<=n;i++) zh[i]=i;
ans=(ans+kyx())%p;
for (int i=1;i<=m;i++)
{
for (int i=1;i<=n;i++) zh[i]=read();
ans=(ans+kyx())%p;
}
cout<<ans*ksm(m+1,p-2)%p;
return 0;
}
//Now,only God know
//但行好事,莫问前程