AtCoder Beginner Contest 327 G. Many Good Tuple Problems(带标号二分图计数+有区别小球放入有区别盒子)

题目

一个长为n(n<=30)的原始序列x,x[i]可以取值0或1

一个长为m(m<=1e9)的点对序列(s,t),

s序列第i项和t的第i项(s_{i},t_{i}),均可以取值[1,n],

如果构造好s和t后,对任意(s_{i},t_{i})都存在01序列x使得x_{s_{i}}\neq x_{t_{i}}

则称这个序列是合法的,问n^{2*m}种(s,t)序列中,有多少合法序列,

答案对998244353取模

思路来源

官方题解

https://www.cnblogs.com/chasedeath/p/14567667.html

题解

考虑1-n共n个点的不含自环的有向图,C_{n}^{2}条边,如果可以用(i,j)边,表示x[i]\neq x[j]

而最终x是要被化成两堆的,一堆0一堆1,也就是一个二分图

要求二分图的某一个带标号的边集方案只能被计一次,

有向图不好统计,可以考虑看成无向图,也就是(1,2)和(2,1)看成一种边

最后填入m个位置后再决定翻不翻转,再乘上对应的2^m种选法

所以,需要统计的是有标号二分图的方案数

有标号二分图计数

g[i][j]表示i个点j条边的二分图方案数,允许重复

g[i][j]=\sum_{k=1}^{i-1}C_{i}^{k}C_{k*(i-k)}^{j}

即枚举二分图左边选了k个点,右边选了i-k个点,k*(i-k)条边里选j条,

这样的话,一个有着t个连通块的二分图会被计数2^t次,

因为对应连通块部分可以左右互换,从而在另一种合法答案中被统计到

于是考虑怎么去重

h[i][j]表示i个点j条边连通的二分图,

然而连通块数定义在状态里不好用于转移,所以后续的计数考虑容斥

通过g[i][j]减掉不合法的方案,

枚举最后一个点所在的连通块多大,

对应连通块和之前的二分图是在什么时候断裂的

当大小为k时,从i-1个点中选k-1个点,再对应选出一些边

h[i][j]=g[i][j]-\sum_{k=1}^{i-1}\sum_{l=0}^{j}C_{i-1}^{k-1}*h[k][j]*g[i-k][j-l]

有了联通的二分图之后,再考虑如何合并

如上文所说,一个有着t个连通块的二分图会被计数2^t次,

而h方案是连通的,即一个连通块会被计数2次,

左右各一次,所以除以2是实际的方案数

f[i][j]表示i个点j条边由若干个连通块组成、无重复的二分图

转移仍然枚举最后一个点所在的连通块多大,

从之前的f合法方案,通过背包转移到新的合法方案

当大小为k时,从i-1个点中选k-1个点,再对应选出一些边

f[i][j]=\frac{h[i][j]}{2}+\sum_{k=1}^{i-1}\sum_{l=0}^{j}C_{i-1}^{k-1}*\frac{h[k][l]}{2}*f[i-k][j-l]

求出f[i][j]后,n个点是固定的,

因为f[n-1][j]可以通过不用边的方式转移到f[n][j]

所以,只需要用到f[n][j]的状态,无需再遍历f[i

当有j条边时,需要满足j条边填到m个位置,j条边都至少出现一次,不然计数就会有重复

小球放盒问题
方法一

这等价于m个有区别的小球放入j个有区别的盒子,每个盒子不能为空

而第二类斯特林数S(m,j)为m个有区别的小球放入j个无区别的盒子的方案数,

所以求出乘上j个盒子的顺序即可,代码中用dp2[i]表示

方法二

互换小球和盒子,

dp[i]表示恰有i种球被放到了m个盒子里,每个盒子只能放一个球

那么就需要用全量的情况,减掉不合法的情况

dp[i]=i^m-\sum_{j=1}^{i-1}C_{i}^j*dp[j]

代码

// Problem: G - Many Good Tuple Problems
// Contest: AtCoder - HHKB Programming Contest 2023(AtCoder Beginner Contest 327)
// URL: https://atcoder.jp/contests/abc327/tasks/abc327_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<>=1,x=1ll*x*x%mod){
		if(n&1)res=1ll*res*x%mod;
	}
	return res;
}
int C(int x,int y){
	if(x<0 || y<0 || x

你可能感兴趣的:(图论,组合数学(容斥原理),知识点总结,图计数,二分图计数,容斥)