【HNOI2008】【BZOJ1004】【洛谷P1446】Cards(Burnside)(背包)

BZOJ传送门

洛谷传送门


解析:

首先题目告诉我们给的置换就是一个群,那么可以直接上Burnside。

发现这个染色有个数限制,不能用Polya。

那么怎么求置换不动点个数?

显然我们只要求一个循环里面的所有点染上相同的颜色,把所有循环以及循环的长度求出来,然后做背包方案数就行了。

由于懒得写DP就直接上记搜了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc getchar
#define cs const

namespace IO{
	template<typename T>
	inline T get(){
		re char c;
		while(!isdigit(c=gc()));re T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int getint(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

int mod;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline void Inc(int &a,int b){(a+=b)>=mod?a-=mod:a;}
inline int dec(int a,int b){return a<b?a-b+mod:a-b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int quickpow(int a,int b,int res=1){
	while(b){
		if(b&1)res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}

int f[65][25][25][25];
int vis[65][25][25][25],idx=-1;
int siz[65],a[65];
bool cur[65];

inline int dp(int now,int a,int b,int c){
	if(!now)return 1;
	if(vis[now][a][b][c]==idx)return f[now][a][b][c];
	vis[now][a][b][c]=idx;
	int &res=f[now][a][b][c];
	res=0;
	if(a>=siz[now])Inc(res,dp(now-1,a-siz[now],b,c));
	if(b>=siz[now])Inc(res,dp(now-1,a,b-siz[now],c));
	if(c>=siz[now])Inc(res,dp(now-1,a,b,c-siz[now]));
	return res;
}

int sa,sb,sc,m,p,n,ans;
signed main(){
	sa=getint(),sb=getint(),sc=getint(),m=getint(),mod=getint();
	n=sa+sb+sc;
	for(int re i=1;i<=n;++i)siz[i]=1;
	ans=dp(n,sa,sb,sc);
	for(idx=1;idx<=m;++idx){
		memset(cur+1,0,sizeof(bool)*n);
		for(int re i=1;i<=n;++i)a[i]=getint();
		int cnt=0;
		for(int re i=1;i<=n;++i)if(!cur[i]){
			siz[++cnt]=0;
			for(int re j=i;!cur[j];j=a[j])cur[j]=true,++siz[cnt];
		}
		Inc(ans,dp(cnt,sa,sb,sc));
	}
	cout<<quickpow(m+1,mod-2,ans)<<"\n";
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值