状压(集合幂级数)数符合某些性质的导出子图/边子图数。

APIO2025 记录

集合幂级数在子图计数问题上的应用-cxy.pdf

容斥技巧:图计数。

集合幂级数基础运算。

限制分为无向图的连通性和有向图的强连通性,以及双连通性。

要做的比 好,需要集合幂级数的运算。

连通性

可以容斥为总方案数减枚举一个包含最小点的子集不连通的方案数。复杂度 。

abc236h。

“已知要求连通的信息推及不要求连通的信息” 及其逆过程。

考虑 exp/ln 的组合意义:设 表示导出子图 的权值, 表示 的所有划分方案的权值和,划分方案的权值为划分出的子集的权值积。有 。

数连通子图

计数边子图使得是连通图。

。

设 为 的导出子图的边子图有多少是连通图,。则 恰好考虑 的导出子图的边子图个一次,。 反推 。

数连通二分子图

计数边子图使得连通且是二分图。

。

枚举左部点 ,右部点 ,左右任意连。

忽略连通限制,原图的一个二分子图被计入 次, 为连通块数。所以 exp 每拼上一个子图都要乘 。

设 为 的答案乘 ,,子集卷积求 ,。

数杏仁图

给定杏仁的两极 ,对于每个 计数边子图使得是杏仁且 。

。

把从 出发的所有链状压求出来 。把这些链挖掉两极 exp 起来。询问时枚举 所在的链和 剩下的任意组合的方案相乘。

Q6954

计数边子图使得连通且所有环的交非空。

。

两个环交于大于两个点会形成新的大环,这三个环至多交于 点。

所以合法的状态有:树、基环树、 个点使得删去后图中无环。容斥掉树上删点也无环:。

子集的生成树数可以矩阵树 。也可以依次对于每个 ,算 的方案数,即把 的子集乘上与 的连边数 exp 起来,复杂度 。

基环树是枚举环上的点集 ,先状压出环上排列的方案数,再把环缩成一个点做生成树计数。每个基环树有一个与环大小相关的容斥系数。复杂度 。

删一个点图中无环,枚举删 ,剩下的子集内部有生成树数,还有至少一条连向 ,exp 起来。复杂度 。

删两个点图中无环,枚举删 ,每个剩下的子集选至多一条边与 连,不能没有,exp 起来,复杂度 。还有一种数重的情况,变成 和 不连通,即两个树。

可以用 的集合幂级数代替 的枚举子集。

Q13329

计数边子图使得是仙人掌

仙人掌看作若干个 的环拼起来。 表示 中点组成环的方案数,可以状压。

枚举连接点 ,把一些包含 的元素组合起来,即挖掉 之后 exp。

复杂度 。

图 k 匹配

加速到 。

强连通性

缩点后为 DAG,对 零度点 容斥,枚举零度点集合,容斥系数 为 。

有一些有意思的事情:比如可达性的容斥,把零度点当成 bfs 的最浅层点,系数也是 ,但是显然有更简单的做法。

无向图重定向为 DAG 的方案数

可以看成从空集开始每次加上一个集合(有序),集合带有 的系数。对于集合幂级数 , 且 。,等价于求 在全集的系数。求逆。

重塑时光

设 表示 分为 个非空段形成 DAG 的方案, 为 分为 个非空段段间没有边的方案数。容斥系数同 DAG 计数,可以做到 。设 ,,拉插之后算系数,复杂度 。

主旋律

数多少个边子集,删去之后图强连通。

设 表示强连通的方案, 表示若干个 度点拼起来的方案,交替转移。

先算 ,再算 ,再给 加上 。

岁月

数多少个边子集,删去之后图的最小外向生成树权值等于图的最小生成树。

here。

对于边权全相同的边,就是 主旋律。

按边权从小到大加入边,旧图的根集当作点,魔改一下。

双连通性

待补。其实是曾经 贺 过。

点双连通生成子图计数

连通边子图是边子图的 ln。

每次再减去 是割点的图,即挖掉 再求 ln 再放回去。

边双连通生成子图计数

边双是没有大小为 的点双的图。那可以求出点双的,然后去掉点数 的,然后枚举割点 exp 拼起来。

另一种更可拓展的做法是 边双连通-连通 变换。

对于正向的变换:有权值 ,一个连通树形结构,每个点是一个集合,权值定义为所有点的 ,已知合成一个点的方案数是 ,求最后的权值和。在这里是把所有边双缩为点,有权值 ,生成边双的方案数 ,连通边子图权值为这样一个树形结构权值和。

设 表示只允许存在两端编号 的割边的权值和,。枚举 , 会被 出去的割边划分为包含 的 和若干 ,,其中 是 向 的边数。对 exp 然后子集卷积一次。

然后反过来。如果给定的 ,则得到的 是连通边子图的数量,倒着做到 即为 。

假设已知 ,则所有 的 ,然后 的部分 。

吓哭了,之前根本没学明白。,等价于每乘一条边多一个常数 算到 里,其他是完全一样的。所以正着变换等于逆着变且 取反。记 表示正着变换,则对于连通子图数 ,边双子图数为 。

void trans(int *dp,int c){
	for(int i=n-1;i;i--){
		for(int s=0;s<(1<<i);s++){
			for(int t=0;t<(1<<n-i-1);t++){
				tmpg[s|(t<<i)]=dp[s|(1<<i)|(t<<i+1)];
				int num=__builtin_popcount(s&e[i]);
				tmph[s|(t<<i)]=c*dp[s|(t<<i+1)]%mod*num%mod;
			}
		}
		xorexp(tmph,tmph,n-1);
		xormul(tmpg,tmph,tmpg,n-1);
		for(int s=0;s<(1<<i);s++){
			for(int t=0;t<(1<<n-i-1);t++){
				dp[s|(1<<i)|(t<<i+1)]=tmpg[s|(t<<i)];
			}
		}
	}
}
void split(int *dp,int c){
	for(int i=1;i<n;i++){
		for(int s=0;s<(1<<i);s++){
			for(int t=0;t<(1<<n-i-1);t++){
				tmpg[s|(t<<i)]=dp[s|(1<<i)|(t<<i+1)];
				int num=__builtin_popcount(s&e[i]);
				tmph[s|(t<<i)]=mod-c*dp[s|(t<<i+1)]%mod*num%mod;
			}
		}
		xorexp(tmph,tmph,n-1);
		xormul(tmpg,tmph,tmpg,n-1);
		for(int s=0;s<(1<<i);s++){
			for(int t=0;t<(1<<n-i-1);t++){
				dp[s|(1<<i)|(t<<i+1)]=tmpg[s|(t<<i)];
			}
		}
	}
}

之前没想明白 贺 的代码,分别实现的正、反变换,事实上显然有 。

还有一种正着证的方法。直接考虑 的结构,首先是树形结构,所以每个边双不会跨过树边;然后如果一些边双缩成一个点,考虑一个桥,贡献为 和直接拆开为新树边 ,抵消了。所以只能是边双。

【UR #30】交通管制

有 条限制: 路径上至少有 条简单路径。

求满足条件的边子图数。

最后的图是若干极大的单点形成的连通块和若干大小 的边双,其中对于 的 不能属于同个单点连通块。

设 表示 的生成森林数,其中包含 的 的 。 为按上面求出来的边双数。

然后对一堆 和 连边,做生成树计数,要求不能有 之间的边。这个即先钦定一些 连边,权值 ,剩下的乱连,权值为 。即 。

最后可能不连通,再 exp 一下。

P11567 建造军营 II

有 个限制,一个边子图的权值为 ,其中 为 且不存在 的边不重复路径经过 的数量。

求边子图权值和。

把每个 路径上所有边双染黑,剩下的白边有 的权值。

黑连通块要求:必须同时包含整对的 ;不能存在一个桥没有被 对覆盖。初始 为满足第一个的连通边子图,做 之后可以得到黑连通块,这个可以对每个 中可能的桥分析,如果没有被 覆盖贡献就被抵消。

白边双要求不能含有 ,选一条边为 ,还可以不选 ,即对 的 ln 做 。

然后把黑白的加起来做 。