圆方树

定义

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

点双连通:若无向图中点对 \(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 <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;
}
posted @ 2024-08-21 21:33  yabnto  阅读(7)  评论(0编辑  收藏  举报