圆方树

定义

割点:无向图中,若删除点及其连边,连通块变多,那么被删除的点为割点

点双连通:若无向图中点对 \(x,y\),删除任意非 \(x\) 和非 \(y\) 节点后,\(x\)\(y\) 任然连通,陈 \(x,y\) 点双连通

点双连通子图:无向图中的一个子图 \(G\)\(G\) 中任意 2 点都是联通的,那么称 \(G\) 为原图的点双连通子图子图

点双连通分量:无向图中的极大点双连通子图,称为 V-DCC

点双连通不具有传递性,边双连通具有传递性

性质

  1. 无向图至少有 3 个点,才可能有割点
  2. dfs 搜索树中的根结点有 2 个及以上的子节点时,才可能是割点
  3. 1 个割点可能在多个点双中

圆方树

定义:将无向图转化为树形结构,解决“必经点”问题的数据结构,通过构建一棵树,使得书上两点路径上的点都是原图的必经点

圆点:原无向图 \(G\) 中的点,仍然保留在圆方数中,称之为圆点

方点:将每个点双内的圆点连边

树边:每个方点向对应点双内的圆点连边

性质

  1. 圆方数中的总点数 \(= 原图点数 + 点双个数\),上限为 \(2 \times n + 1\)
  2. 圆点是被方点隔开的,一条边的两个端点一定是圆点和方点
  3. 圆点的度数就是包含该点的点双个数。
  4. 圆方树删除点 \(x\) 后剩余节点的连通性与原图中删除 \(x\) 后的连通性等价。
  5. 原图中 \(x\)\(y\) 的路径的必经点就是圆方数上 \(x\)\(y\) 经过的所有的圆点
  6. 圆点为割点时才可能有超过一个儿子结点

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 <iostream>
#include <cmath>
#include <map>

using namespace std;
using pil = pair<int, int>;

const int MaxN = 120000, MaxK = 23;

struct S {
  int to, w, nxt;
} e[MaxN << 1], g[MaxN << 1];

int 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;
		if (j == (fe ^ 1)) continue;
		if (!dfn[i]) {
			f[i][0] = x;
			build(i, j);
			low[x] = min(low[x], low[i]);
			if (low[i] >= dfn[x]) {
				id++;
				res[id] = www[x][st[tot].first];
				for (int k = tot; st[k].first != x; k--) {
					dis[st[k].first][id] = res[id], res[id] += st[k].second;
				}
				dis[x][id] = res[id];
				for (add(id, x, min(res[id] - dis[x][id], dis[x][id])), st[tot + 1] = {0, 0}, sz[id] = 1; st[tot + 1].first != i; tot--) {
					add(id, st[tot].first, min(res[id] - dis[st[tot].first][id], dis[st[tot].first][id])), sz[id]++;
				}
			}
		} else if (dfn[i] < dfn[x]) {
			low[x] = min(low[x], dfn[i]);
		}
	}
}

int W(int x, int y, int id) {
	int tmp = abs(dis[x][id] - dis[y][id]);
	if (sz[id] == 2) return tmp;
	return min(tmp, res[id] - tmp);
}

void DFS(int x, int fe = 0) {
	f[x][0] = g[fe ^ 1].to;
	for (int i = 1; i < MaxK; i++) {
		f[x][i] = f[f[x][i - 1]][i - 1];
	}
	for (int j = dh[x]; ~j; j = g[j].nxt) {
		int i = g[j].to;
		if (j == (fe ^ 1)) continue;
		grtd[i] = grtd[x] + g[j].w, d[i] = d[x] + 1;
		DFS(i, j);
	}
}

int query(int x, int y) {	
	for (int i = MaxK - 1; ~i; i--) {
		if (f[x][i] && d[f[x][i]] >= d[y]) x = f[x][i];
	}
	return x;
}

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; i--) {
		if (f[x][i] && f[y][i] && f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
	}
	return {x, y};
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	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;
    if (u == v) continue;
    if (www[u].count(v)) {
      www[u][v] = www[v][u] = min(www[u][v], w);
      continue;
    }
    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 u, v, x, y; q; q--) {
		cin >> u >> v;
		pil p = lca(u, v);
		x = p.first, y = p.second;
		if (x == y) {
			cout << grtd[u] + grtd[v] - 2 * grtd[x] << '\n';
		} else if (x > n && y > n) {
			cout << grtd[u] + grtd[v] - 2 * grtd[f[x][0]] << '\n';
		} else {
			cout << grtd[u] + grtd[v] - grtd[y] - grtd[x] + W(x, y, f[x][0]) << '\n';
		}
	}
	return 0;
}
posted @ 2024-08-21 21:33  yabnto  阅读(33)  评论(0)    收藏  举报