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

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 做

然后把黑白的加起来做