广义串并联图 杂记

pakencamp 遇到了 7 月在成都 hba 模拟赛里的广义串并联图题,决定把这玩意学一学。

广义串并联图:不存在四个点 \(a,b,c,d\) 满足其间存在六条边不交的简单路径的图。

广义串并联图可以通过不断进行以下操作把整个图缩成一个点:

  • 删一度点。删去图中某个度数为 \(1\) 的点。
  • 缩二度点。将一个二度点缩成一条边。
  • 叠合重边。将两条重边缩成一条。

相比于广义串并联图本身,OI 中更常见的是这种缩图的方法:这样缩完之后满足所有点度数大于等于 \(3\),即 \(3n \ge 2m\)。如果一张无向连通图满足 \(m - n \le k\),那么结合上面式子可以解出 \(n \le 2k,m \le 3k\)

考虑这样缩完得到的新图有什么含义:新图中的一条边代表一个子图,且这个子图和其它图只通过这两个点连接;新图中的一个点也代表一个子图,这个子图是原图不断删一度点形成的。

Grand Graph

  • 给定一张 \(n\) 个点 \(m\) 条边的无向连通图,每个点可以染上 \(1 \sim k\) 之间的颜色,问有多少染色方案使得一条边连接的两个点颜色不同。
  • \(1 \le n,m \le 10^5,m - n \le 3\)

广义串并联图上一般的 DP 方法是对边 DP 并记录边两端的点的状态。

本题中,我们设 \(f_i\)\(g_i\) 表示第 \(i\) 条边两端颜色不同/相同的方案数(一条边实际上是一张子图,子图的两个端点颜色可能相同)。初始时应有 \(f_i = 1,g_i = 0\)。注意这里方案数只统计被缩掉的点,而不包含边的两个端点。

考虑三种操作带来的转移:

  • 删一度点。一度点的贡献个图的其它部分没有关系,直接贡献到答案上。转移 \(ans \gets ans \cdot ((k - 1)f_i + g_i)\)
  • 缩二度点。考虑合并 \((x,y),(y,z)\) 两条边。
    • 先考虑 \(g\) 的转移:我们希望 \(c_x = c_z\),那么既可以 \(c_x = c_y\) 也可以 \(c_x \ne c_y\),第二种情况下 \(y\) 点的颜色有 \(k- 1\) 个可以选择的值。故 \(g = g_{a}g_{b} + (k-1)f_af_b\)
    • \(f\) 的转移类似:如果 \(c_x \ne c_y \ne c_z\),那么 \(y\)\(k-2\) 种可能的颜色;否则如果 \(c_x = c_y\)\(c_y = c_z\)\(y\) 的颜色都已经被确定。故 \(f = (k-2)f_af_b+f_ag_b + f_bg_a\)
  • 叠合重边。这个最简单,乘起来即可。

缩完图后图中最多还有 \(6\) 个点,\(\mathcal O(k^k)\) 爆搜即可。

广义串并联图的实现常使用 queue 维护一二度点,用 map 维护出边。代码实现:

for(int i = 1;i <= m;i ++){
    cin >> u >> v,d[u] ++,d[v] ++;
    mp[u][v] = mp[v][u] = ++ct,f[ct] = 1,g[ct] = 0;
}for(int i = 1;i <= n;i ++) if(d[i] <= 2) q.push(i);
while(q.size()){
    int u = q.front(); q.pop();
    if(d[u] <= 0) continue;
    if(d[u] == 1){
        auto [v,w] = *mp[u].begin();
        删一度点;
        mp[v].erase(u),d[u] = 0;
        if((--d[v]) <= 2) q.push(v);
    }else{
        assert(d[u] == 2);
        auto [x,a] = *mp[u].begin();
        auto [y,b] = *--mp[u].end();
        缩二度点;
        if(mp[x][y]){叠合重边;}
        else mp[x][y] = mp[y][x] = ++ct,d[x] ++,d[y] ++;
        mp[x].erase(u),mp[y].erase(u),d[u] = 0;
        if((-- d[x]) <= 2) q.push(x);
        if((-- d[y]) <= 2) q.push(y);
    }
}

P6790 [SNOI2020] 生成树

  • 给定无向连通图 \(G\),已知 \(G\) 在删掉一条边后是一颗仙人掌(仙人掌:不存在两个拥有公共边的简单环的无向联通图),求 \(G\) 的生成树个数。结果对 \(998244353\) 取模。
  • \(1 \le n \le m \le 2 \cdot 10^5\)

\(G\) 的环数很少,进一步发现图 \(G\) 是广义串并联图。

我们关心的其实只有两种本质不同的子图:一种是选择的生成树边连接了两端点的路径的,另一种不存在。记两者的方案数为 \(f_i,g_i\)

考虑三种操作带来的转移:

  • 删一度点。一度点 \(v\) 必须和 \(u\) 连通,所以答案直接乘上 \(f_i\)
  • 缩二度点。考虑合并 \((x,y),(y,z)\) 两条边。如果仍然要连通那么 \((x,y),(y,z)\) 都需要连通,否则只需要存在一条不连通的边即可。转移为 \(f = f_af_b,\;g = g_ag_b+f_ag_b+f_bg_a\)
  • 叠合重边。如果要求不连通那么需要两条重边都不连通;否则,需要恰好有一个连通(两个都联通会成环)。转译为 \(f = f_ag_b+f_bg_a,\;g=g_ag_b\)

缩到只剩一个点即为答案。

P4426 [HNOI/AHOI2018] 毒瘤

  • 给定 \(n\) 个点 \(m\) 条边的无向连通图,求其有多少个独立集。
  • \(1 \le n,m \le10^5,m - n \le 10\)

这道题和前面两题的最大区别是,贡献不能在删除一度点时直接计算,原因是一度点 \(v\) 的贡献和其连接的 \(u\) 点取值有关(在 Grand Graph 中,不管 \(u\) 的颜色是什么,\(v\) 的贡献总是固定的)。

\(f_{i,0/1/2/3}\) 表示第 \(i\) 条边两端的选择状态为 \(00,01,10,11\) 的方案数,\(g_{i,0/1}\) 表示第 \(i\) 个点选择 \(0/1\) 时,其通过删一度点合并上来的子图的总方案数。

考虑三种操作带来的转移:

  • 删一度点。一度点 \(u\) 会对其连接的 \(v\)\(g_v\) 造成影响。

\[\begin{cases} g_{v,0} = g_{v,0}(f_{i,0}g_{u,0}+f_{i,1}g_{u,1}) \\ g_{v,1} = g_{v,1}(f_{i,2}g_{u,0}+f_{i,3}g_{u,1}) \end{cases}\]

  • 缩二度点。考虑合并 \((x,y),(y,z)\) 两条边。分讨 \(y\) 的值是什么,然后枚举状态转移。记得乘上 \(g_{y,0/1}\) 的值。
  • 叠合重边。直接乘起来即可。

完成缩图后图中最多还有 \(20\) 个点 \(30\) 条边,直接 \(\mathcal O(k2^{2k})\) 枚举即可。

QOJ 7790 最短路求和

  • 给定 \(n\) 个点 \(m\) 条边的无向连通图,求所有点对的最短路之和。
  • \(1 \le n,m \le 10^5,m - n \le 1000\)

注意到本题如果直接像上面一样处理,叠合重边的过程会很困难,我们无法通过记录很少的信息来得到新的子图的最短路。

考虑首先删去所有 \(1\) 度点,把 \(\ge 3\) 度点看做关键点,那么关键点的个数显然是 \(\mathcal O(n -m)\) 的。

关键点之间的路径一定是一条链,可以很方便的求出关键点之间的全源最短路,也可以处理出来每个非关键点是在哪两个关键点的路径上。

一度点之间的贡献应当比较好求出。跨过关键点的贡献也比较好计算。主要难度是计算同一个子图里的贡献。

考虑枚举两个关键点 \(x,y\) 之间的二度点,在同一条链上顺次枚举 \(u\)。考察 \(u\) 到其他链上 \(v\) 的距离,路径只可能有四种:\(u \to x \to (y) \to v,u \to y \to (x) \to v\)。当然注意到 \(u \to x\) 是直接走还是绕道 \(y\)\(v\) 无关,所以在 \(v\) 上的决策只有走 \(u\) 还是走 \(v\),而这两者应当是前后缀,可以在 \(u\) 移动时暴力移动。这部分的复杂度应当有一个比较松的上界 \(\mathcal O(n(m-n))\)

posted @ 2024-10-11 11:23  sunkuangzheng  阅读(12)  评论(0编辑  收藏  举报