枚举连通块拆贡献+容斥:ABC321G

https://atcoder.jp/contests/abc321/tasks/abc321_g

这种题都没看出来我要去退役了

看完题目,可以获得:

  1. 期望、连通块:显然拆贡献啊!
  2. n ≤ 17 n\le 17 n17:这不明显状压?结合前面连通块,就是枚举连通块啊!
  3. 继续分析第2点,17这么小,显然枚举子集。为啥枚举?计数题只有容斥这个套路啊!

然后就很裸了。

为啥容斥?因为拆完贡献后要使得恰好为一个连通块。

这里还有个小套路。容斥是为了不重不漏,我们可以枚举最编号小点所在的单个连通块。剩下随便连。

	n=read(); m=read(); init(m); 
	for(i=1; i<=m; ++i) k=read(), R[1<<k-1]++; 
	for(i=1; i<=m; ++i) k=read(), B[1<<k-1]++; 
	for(s=1; s<(1<<n); ++s) {
		for(i=1; i<=n; ++i) if(s&(1<<i-1)) break; 
		R[s]=R[s-(1<<i-1)]+R[1<<i-1]; 
		B[s]=B[s-(1<<i-1)]+B[1<<i-1]; 
		if(R[s]==B[s]) {
			f[s]=g[s]=fac[R[s]]; 
			for(t=(s-1)&s; t; t=(t-1)&s) 
				if(t&(1<<i-1)) Add(g[s], -g[t]*f[s-t]); 
			Add(ans, g[s]*fac[m-R[s]]); 
		}
	}
	ans=(ans*ifac[m]%mo+mo)%mo; 
	printf("%lld", ans); 
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值