bzoj 1004: [HNOI2008]Cards burnside引理+dp

题意

有n张卡片,m个置换,要给每张卡片染上三种颜色,每种颜色的个数固定,求有多少种本质不同的染色方案。

分析

burnside引理:本质不同的染色方案= 1 ∣ G ∣ ( c ( a 1 ) + c ( a 2 ) + . . . + c ( a k ) ) \frac{1}{|G|}(c(a1)+c(a2)+...+c(ak)) G1(c(a1)+c(a2)+...+c(ak))

G表示置换的集合,ai表示置换(i=1,2…,k),c(ai)表示置换的不动点数量。

对于这题,我们可以把每种染色方案看成是一个点,然后对于一个置换,若一种染色方案经过某个置换后不变,那么这个染色方案的成为该置换的不动点。

显然一个置换下的每一个循环内的点的颜色都要是一样的。

那么问题就转换成了求每种置换下每个循环染的颜色都相同的不同染色方案有多少种,可以用一个三维背包来做,加起来除以置换数量即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 105
#define LL long long
using namespace std;

int s1,s2,s3,n,m,MOD,f[N][N][N],s[N],vis[N],a[N][N];

int ksm(int x,int y)
{
	int ans=1;
	while (y)
	{
		if (y&1) ans=(LL)ans*x%MOD;
		x=(LL)x*x%MOD;y>>=1;
	}
	return ans;
}

int dp(int x)
{
	for (int i=1;i<=n;i++) vis[i]=0;
	int tot=0;
	for (int i=1;i<=n;i++)
		if (!vis[i])
		{
			int p=i;vis[i]=1;s[++tot]=1;
			while (!vis[a[x][p]])
			{
				p=a[x][p];vis[p]=1;s[tot]++;
			}
		}
	for (int i=0;i<=s1;i++)
		for (int j=0;j<=s2;j++)
			for (int k=0;k<=s3;k++)
				f[i][j][k]=0;
	f[0][0][0]=1;
	for (int l=1;l<=tot;l++)
		for (int i=s1;i>=0;i--)
			for (int j=s2;j>=0;j--)
				for (int k=s3;k>=0;k--)
				{
					if (i>=s[l]) f[i][j][k]=(f[i][j][k]+f[i-s[l]][j][k])%MOD;
					if (j>=s[l]) f[i][j][k]=(f[i][j][k]+f[i][j-s[l]][k])%MOD;
					if (k>=s[l]) f[i][j][k]=(f[i][j][k]+f[i][j][k-s[l]])%MOD;
				}
	return f[s1][s2][s3];
}

int main()
{
	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 ans=0;
	for (int i=1;i<=m;i++)
		ans=(ans+dp(i))%MOD;
	ans=(LL)ans*ksm(m,MOD-2)%MOD;
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值