08.21总结
圆方树
引入
我们注意到,树结构相比普通图具有诸多优良特性。若能将在无向图上求解的问题转化为树结构问题,往往能大幅简化求解过程。圆方树正是实现这一转化的有效工具。
定义
我们称原图中的点为"圆点"。通过引入方点并调整边的关系,可以构造出一棵树。通过合理赋权,使这棵树能够保持原图的某些特性,从而将原问题转化为树上的问题。具体构建过程如下:对于每个点双连通分量,首先删除其中所有圆点之间的直接连接边。然后为该点双新增一个方点,并将该点双内的所有圆点都与这个方点相连。这样构建出的图是一个无环的连通图,即所谓的圆方树。构建过程本身并不复杂,只要掌握点双连通分量的求法即可完成。而难点在于:如何恰当设置权值,使得在圆方树上能够求解原问题。
代码
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,初始值为nvn[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× 经过该点的路径数)来高效求解。