定义
割点:无向图中,若删除点及其连边,连通块变多,那么被删除的点为割点
点双连通:若无向图中点对 \(x,y\),删除任意非 \(x\) 和非 \(y\) 节点后,\(x\) 和 \(y\) 任然连通,陈 \(x,y\) 点双连通
点双连通子图:无向图中的一个子图 \(G\),\(G\) 中任意 2 点都是联通的,那么称 \(G\) 为原图的点双连通子图子图
点双连通分量:无向图中的极大点双连通子图,称为 V-DCC
点双连通不具有传递性,边双连通具有传递性
性质
- 无向图至少有 3 个点,才可能有割点
- dfs 搜索树中的根结点有 2 个及以上的子节点时,才可能是割点
- 1 个割点可能在多个点双中
圆方树
定义:将无向图转化为树形结构,解决“必经点”问题的数据结构,通过构建一棵树,使得书上两点路径上的点都是原图的必经点
圆点:原无向图 \(G\) 中的点,仍然保留在圆方数中,称之为圆点
方点:将每个点双内的圆点连边
树边:每个方点向对应点双内的圆点连边
性质
- 圆方数中的总点数 \(= 原图点数 + 点双个数\),上限为 \(2 \times n + 1\)
- 圆点是被方点隔开的,一条边的两个端点一定是圆点和方点
- 圆点的度数就是包含该点的点双个数。
- 圆方树删除点 \(x\) 后剩余节点的连通性与原图中删除 \(x\) 后的连通性等价。
- 原图中 \(x\) 到 \(y\) 的路径的必经点就是圆方数上 \(x\) 到 \(y\) 经过的所有的圆点
- 圆点为割点时才可能有超过一个儿子结点
code
代码形如 tarjan
void DFS(int x, int fa = -1) {
dfn[st[++tot] = x] = low[x] = ++sum;
for (int i : t[x]) {
if (i == fa) continue;
if (!dfn[i]) {
DFS(i, x);
low[x] = min(low[x], low[i]);
if (low[i] >= dfn[x]) {
ans1++;
for (g[++id].push_back((g[x].push_back(id), x)), indeg[id]++, indeg[x]++, st[tot + 1] = 0, sz[id] = 1; st[tot + 1] != i; tot--) {
g[id].push_back(st[tot]), g[st[tot]].push_back(id), indeg[st[tot]]++, indeg[id]++, sz[id]++;
}
}
} else {
low[x] = min(low[x], dfn[i]);
}
}
}
仙人掌
用圆方树来处理仙人掌
P5236
对于一组询问 \((x, y)\),转化为求圆方树上 \(x\) 到 \(y\) 的路径,定义两点距离(\(dis\))为两点再环上的较短路,到根的距离我们也用 \(dis\),不过不写根。
将圆点到方点的边权,设为圆点到方点的父亲的距离,而方点到父亲没有边权
若 \(lca(x,y)\) 为圆点,有 \(dis_x+dis_y-2\times dis_{lca}\)
若 \(lca(x,y)\) 为方点,将式子改为:\(dis_x-dis_{x'}+dis_y-dis_{y'}+dis(x,y)\)
\(dis\) 我们选择用再换上跑前缀和的方式来处理
code
#include <algorithm>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
using ll = long long;
using pil = pair<int, ll>;
const int MaxN = 200010, MaxK = 19;
struct S {
ll to, w, nxt;
} e[MaxN << 1], g[MaxN << 1];
ll f[MaxN][MaxK], hd[MaxN], sz[MaxN], dh[MaxN], dfn[MaxN], low[MaxN], res[MaxN], grtd[MaxN], d[MaxN], cnt = 1, cg = 1, sum, tot, n, m, id, q;
pil st[MaxN];
map<int, int> dis[MaxN], www[MaxN];
void add(int u, int v, int w) {
g[++cg] = {v, w, dh[u]}, dh[u] = cg;
g[++cg] = {u, w, dh[v]}, dh[v] = cg;
}
void build(int x, int fe = 0) {
dfn[x] = low[x] = ++sum, st[++tot] = {x, e[fe].w};
for (int j = hd[x]; ~j; j = e[j].nxt) {
int i = e[j].to, w = e[j].w;
if (j == (fe ^ 1)) continue;
if (!dfn[i]) {
build(i, j), f[i][0] = x;
low[x] = min(low[x], low[i]);
if (low[i] >= dfn[x]) {
++id;
res[id] = www[x][st[tot].first];
for (int j = tot; st[j].first != x; j--) {
dis[st[j].first][id] = res[id], res[id] += st[j].second;
}
dis[x][id] = res[id];
for (add(id, x, id), st[tot + 1] = {0, 0}, sz[id] = 1; st[tot + 1].first != i; tot--) {
add(st[tot].first, id, id), sz[id]++;
}
}
} else if (dfn[i] < dfn[x]) {
low[x] = min(low[x], dfn[i]);
}
}
}
ll W(int x, int y, int c) {
ll tmp = abs(dis[x][c] - dis[y][c]);
if (sz[c] == 2) return tmp;
return min(tmp, res[c] - tmp);
}
void DFS(int x, int fe = 0) {
f[x][0] = g[fe ^ 1].to;
for (int j = 1; j < MaxK; j++) {
f[x][j] = f[f[x][j - 1]][j - 1];
}
// cout << x << " <- " << fe << endl;
for (int j = dh[x]; ~j; j = g[j].nxt) {
int i = g[j].to;
if (j == (fe ^ 1)) continue;
if (i <= n) {
g[j].w = W(i, g[fe ^ 1].to, g[j].w);
g[j ^ 1].w = 0;
} else {
g[j].w = g[j ^ 1].w = 0;
}
// cout << i << " " << grtd[x] << " " << g[fe ^ 1].to << " " << x << " " << fe << " " << g[fe].to << " " << g[j].w << endl;
grtd[i] = grtd[x] + g[j].w, d[i] = d[x] + 1;
DFS(i, j);
}
}
int query(int u, int v) {
for (int i = MaxK - 1; ~i; i--) {
if (d[f[u][i]] >= d[v]) {
u = f[u][i];
}
}
return u;
}
pil lca(int x, int y) {
if (d[x] < d[y]) swap(x, y);
x = query(x, y);
if (x == y) {
return {x, y};
}
for (int i = MaxK - 1; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
x = f[x][i], y = f[y][i];
}
}
return {x, y};
}
int main() {
cin >> n >> m >> q, id = n;
fill(hd, hd + MaxN - 1, -1);
fill(dh, dh + MaxN - 1, -1);
for (int i = 1, u, v, w; i <= m; i++) {
cin >> u >> v >> w, www[u][v] = www[v][u] = w;
e[++cnt] = {v, w, hd[u]}, hd[u] = cnt;
e[++cnt] = {u, w, hd[v]}, hd[v] = cnt;
}
build(1);
DFS(1);
for (int i = 1, u, v, x, y; i <= q; i++) {
cin >> u >> v;
pil tmp = lca(u, v);
x = tmp.first, y = tmp.second;
// cout << x << " " << y << endl;
if (x == y) {
cout << grtd[u] + grtd[v] - 2 * grtd[x] << endl;
} else if (x > n && y > n) {
cout << grtd[u] + grtd[v] - 2 * grtd[f[x][0]] << endl;
} else {
cout << grtd[u] + grtd[v] - grtd[x] - grtd[y] + W(x, y, f[x][0]) << endl;
}
}
return 0;
}
标签:int,tot,++,圆方树,low,id,dis
From: https://www.cnblogs.com/ybtarr/p/18303770