08.21总结

圆方树

引入

我们注意到,树结构相比普通图具有诸多优良特性。若能将在无向图上求解的问题转化为树结构问题,往往能大幅简化求解过程。圆方树正是实现这一转化的有效工具。

定义

我们称原图中的点为"圆点"。通过引入方点并调整边的关系,可以构造出一棵树。通过合理赋权,使这棵树能够保持原图的某些特性,从而将原问题转化为树上的问题。具体构建过程如下:对于每个点双连通分量,首先删除其中所有圆点之间的直接连接边。然后为该点双新增一个方点,并将该点双内的所有圆点都与这个方点相连。这样构建出的图是一个无环的连通图,即所谓的圆方树。构建过程本身并不复杂,只要掌握点双连通分量的求法即可完成。而难点在于:如何恰当设置权值,使得在圆方树上能够求解原问题。

代码

cpp 复制代码
void tarjan(int u, int fa) {
    low[u] = dfn[u] = ++tot;
    sta.push(u);
    for (auto v : ve[u]) {
        if (v == fa) continue;
        if (!dfn[v]) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u]) {      // 发现点双
                int now = 0;
                sid++;    // 方点id,初始值为n
                vn[u].push_back(sid);    //建双向边
                vn[sid].push_back(u);
                while (now != v) {
                    now = sta.top();
                    vn[now].push_back(sid);    //建双向边
                    vn[sid].push_back(now);
                    sta.pop();
                }
            }
        } else {
            low[u] = min(low[u], dfn[v]);
        }
    }
}

例题

铁人两项

给定一张无向图,问有多少互不相同三元组<aaa, bbb, ccc>

使得存在一条从 aaa 到 bbb 经过 ccc 的简单路径。

题解

在同一个点双连通分量中,任意两点之间的所有简单路径的并集恰好构成该点双。对于任意两点,其简单路径所经过的点集可表示为路径上各点双的并集。在圆方树模型中,该点集对应为: 两个圆点路径上的所有圆点和路径上方点相邻的所有圆点 。由于限制条件 c≠ac ≠ ac=a 且 c≠bc ≠ bc=b,最终答案为该点集大小减 2。 具体实现时,考虑圆方树上的权值设计: 将方点权值设为相邻圆点数量,并将圆点权值设为 -1(避免相邻方点重复计算),而路径端点不计入贡献。这样,圆方树上两圆点间路径的点权和即为所求答案。问题转化为统计树上所有圆点对的路径权值和,可通过树形 DP 计算每个点的贡献(点权 ×\times× 经过该点的路径数)来高效求解。