【BZOJ1004】[HNOI2008]Cards Burnside引理

【BZOJ1004】[HNOI2008]Cards

题意:把\(n\)张牌染成\(a,b,c\),3种颜色。其中颜色为\(a,b,c\)的牌的数量分别为\(sa,sb,sc\)。并且给出\(m\)个置换,保证这\(m\)个置换加上本身的置换能构成一个置换群,两种染色方案被认为是相同的当且仅当一种方案可以通过某个置换变成另一种。求不同的染色方案数。答案对\(P\)取模。
\(sa,sb,sc\le 20,m\le 60\)

题解:这里对每种颜色都有一个限制,怎么办呢?
回顾从Burnside引理到Pólya定理的推导过程。
如果一个染色方案是不动点,那么它的每个循环中的所有元素的颜色都相同。
所以对于一个置换\(f\),我们找到它的一个循环,大小为\(k\),我们可以将其看成一个大小为\(k\)的物品,然后跑多维背包求出方案数,即为不动点的数目。
最后套用Burnside引理即可。

#include <cstdio>
#include <cstring>
#include <iostream>
int n,m,sa,sb,sc,P;
int ans,f[21][21];
int vis[61],to[61];
inline int solve()
{
	memset(f,0,sizeof(f)),memset(vis,0,sizeof(vis));
	int i,j,a,b,t;
	f[0][0]=1;
	for(i=1;i<=n;i++)	if(!vis[i])
	{
		for(t=0,j=i;!vis[j];vis[j]=1,j=to[j],t++);
		for(a=sa;a>=0;a--)	for(b=sb;b>=0;b--)
		{
			if(a>=t)	f[a][b]+=f[a-t][b];
			if(b>=t)	f[a][b]+=f[a][b-t];
			f[a][b]%=P;
		}
	}
	return f[sa][sb];
}
inline int pw(int x,int y)
{
	int z=1;
	while(y)
	{
		if(y&1)	z=z*x%P;
		x=x*x%P,y>>=1;
	}
	return z;
}
int main()
{
	scanf("%d%d%d%d%d",&sa,&sb,&sc,&m,&P),n=sa+sb+sc;
	int i,j;
	for(j=1;j<=m;j++)
	{
		for(i=1;i<=n;i++)	scanf("%d",&to[i]);
		ans+=solve();
	}
	for(i=1;i<=n;i++)	to[i]=i;
	ans+=solve();
	printf("%d",ans*pw(m+1,P-2)%P);
	return 0;
}

posted @ 2018-01-07 08:38  CQzhangyu  阅读(197)  评论(0编辑  收藏  举报