[关键字]:扩展欧几里得 Burnside定理 动态规划
[题目大意]:给你n张牌,n张牌一共有三种颜色,并且给定这n张牌种的红色、蓝色、绿色各有多少张。现在给定m种洗牌法,每一种牌的排列通过洗牌得到另一种牌的排列,则它们称为本质相同染色法。求:对这n张牌染色,满足sr、sb、sg的限制下,可以通过m种洗牌法,有多少种本质不同的染色法?
//============================================================================================================================
[分析]:Burnside定理:有m个置换k中颜色,所有本质不同的染色方案数就是每种置换的不变元素的个数的平均数。所谓不变元素就是一种染色方案经过置换变换后和没变化之前一样。所以现在就是需要求出不变元素,同时还要满足各种颜色数量的限制。置换的循环在不变元素中一定是一个颜色,然后可以求一个三维的01背包的方案数。而最后的除法需要利用扩展欧几里得求乘法的逆元。
[代码]:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=70;
int s1,s2,s3,n,m,MOD,ans;
int a[MAXN][MAXN],f[MAXN][MAXN][MAXN],d[MAXN];
bool b[MAXN];
void Init()
{
scanf("%d%d%d%d%d",&s1,&s2,&s3,&m,&MOD);
n=s1+s2+s3;
for (int i=1;i<=m;++i)
for (int j=1;j<=n;++j)
scanf("%d",&a[i][j]);
++m;
for (int i=1;i<=n;++i) a[m][i]=i;
}
int DP(int x)
{
memset(f,0,sizeof(f));
memset(b,0,sizeof(b));
int sum=0,p;
for (int i=1;i<=n;++i)
if (!b[i])
{
d[++sum]=1;
b[p=i]=1;
while (!b[a[x][p]])
{
++d[sum];
b[a[x][p]]=1;
p=a[x][p];
}
}
f[0][0][0]=1;
for (int h=1;h<=sum;++h)
for (int i=s1;i>=0;--i)
for (int j=s2;j>=0;--j)
for (int k=s3;k>=0;--k)
{
if (i>=d[h]) f[i][j][k]=(f[i][j][k]+f[i-d[h]][j][k])%MOD;
if (j>=d[h]) f[i][j][k]=(f[i][j][k]+f[i][j-d[h]][k])%MOD;
if (k>=d[h]) f[i][j][k]=(f[i][j][k]+f[i][j][k-d[h]])%MOD;
}
return f[s1][s2][s3];
}
int extend_gcd(int a,int b,int &x,int &y)
{
int temp;
if (b==0)
{
temp=a;
x=1,y=0;
return temp;
}
else
{
temp=extend_gcd(b,a%b,x,y);
int t=x;x=y,y=t-(a/b)*y;
return temp;
}
}
void Work()
{
for (int i=1;i<=m;++i)
ans=(ans+DP(i))%MOD;
//printf("%d\n",ans);
int x,y;
int temp=extend_gcd(m,MOD,x,y);
//printf("%d\n",temp);
int t1=MOD/temp,t2=m/temp;
while (x<=0 || y>0) x+=t1,y-=t2;
printf("%d\n",(ans*x)%MOD);
}
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
Init();
Work();
return 0;
}