状压(集合幂级数)数符合某些性质的导出子图/边子图数。
APIO2025 记录
限制分为无向图的连通性和有向图的强连通性,以及双连通性。
要做的比 好,需要集合幂级数的运算。
连通性
可以容斥为总方案数减枚举一个包含最小点的子集不连通的方案数。复杂度 。
“已知要求连通的信息推及不要求连通的信息” 及其逆过程。
考虑 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 做 。
然后把黑白的加起来做 。