[bzoj1004][HNOI2008]Cards

[bzoj1004][HNOI2008]Cards

标签: 置换 Burnside引理


题目链接

扯淡

题目中说了这样一句话

两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)

怎么样?是不是很棘手。然而

输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。

题意

n张牌,3种颜色,给你一个置换集合G(且保证对于任意\(f,g\in G,fg \in G\)),让你对每张牌染色,使得红色Sred种,蓝色Sblue种,绿色Sgreen种。求不同的染色方案总数。

题解

根据burnside引理:等价类的个数等于每一个置换的不动点的个数之和的平均数。
如果是一个不动点的话,那么这个置换每一个循环中的元素颜色都一样。
这样我们就能跑一个三维背包了。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<vector>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
	int sum=0,p=1;char ch=getchar();
	while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
	if(ch=='-')p=-1,ch=getchar();
	while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
	return sum*p;
}

const int maxn=100;

int mod;

int dp[maxn][maxn][maxn],sr,sb,sg,n,m,ans;

int fa[maxn*3],w[maxn*3],cnt;

int fin(int x)
{
	return x==fa[x]?x:fa[x]=fin(fa[x]);
}

int power(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b & 1)ans=ans*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return ans;
}

void init()
{
	sr=read();sb=read();sg=read();n=sr+sb+sg;
	m=read();mod=read();
	REP(i,1,m+1)
	{
		REP(j,1,n)fa[j]=j;
		if(i<m+1)
		{
			REP(j,1,n)
			{
				int v=read();
				int x=fin(j),y=fin(v);
				if(x!=y)
				{
					fa[y]=x;
				}
			}
		}
		memset(w,0,sizeof(w));
		cnt=0;
		REP(col,1,n)
		{
			int flag=1;
			REP(j,1,n)
			{
				if(fin(j)==col)
				{
					if(flag)flag=0,cnt++;
					w[cnt]++;
				}
			}
		}
		memset(dp,0,sizeof(dp));
		dp[0][0][0]=1;
		REP(j,1,cnt)
		{
			DREP(x,sr,0)
			{
				DREP(y,sb,0)
				{
					DREP(z,sg,0)
					{
						if(x>=w[j])dp[x][y][z]+=dp[x-w[j]][y][z];
						if(y>=w[j])dp[x][y][z]+=dp[x][y-w[j]][z];
						if(z>=w[j])dp[x][y][z]+=dp[x][y][z-w[j]];
						dp[x][y][z]%=mod;
					}
				}
			}
		}
		ans=(ans+dp[sr][sb][sg])%mod;
	}
	cout<<ans*power(m+1,mod-2)%mod<<endl;
}

int main()
{
	freopen("input.in","r",stdin);
	freopen("output.out","w",stdout);
	init();
	return 0;
}


posted @ 2017-09-04 11:19  Deadecho  阅读(123)  评论(0编辑  收藏  举报