首页 > 其他分享 >圆方树

圆方树

时间:2024-08-21 21:40:39浏览次数:3  
标签:int tot ++ 圆方树 low id dis

定义

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

点双连通:若无向图中点对 \(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;
}

标签:int,tot,++,圆方树,low,id,dis
From: https://www.cnblogs.com/ybtarr/p/18303770

相关文章

  • 圆方树学习笔记 & 最短路 题解
    前言圆方树学习笔记,从一道例题讲起。题目链接:Hydro&bzoj。题意简述仙人掌上求两点距离。题目分析为了把仙人掌的性质发挥出来,考虑将其变成一棵树。圆方树就是这样转换的工具。先讲讲圆方树的概念:原图上的点为圆点,每个点双对应一个方点,树边都是方点连向点双内的圆点。具......
  • 圆方树
    定义圆方树:将无向图转化为树形结构的数据结构,使得树上2点路径上的点都是原图的必经点。圆点:原无向图\(G\)中的点,仍然保留在圆方树中,称之为圆点。方点:将每一个点双连通分量新建一个“方点”。树边:每一个方点都向对应的点双内的圆点连边。基本性质:性质一:圆方树的总点数=......
  • 圆方树
    一些概念割点:无向图中,若删除点x及其连边,连通块变多,那么x为割点。点双连通:若点对x和y,删除任意非x和非y节点后,x和y仍然联通,称x和y点双连通。点双联通子图:无向图中的一个子图G,G中任意两点都是点双连通的,那么G为原图的一个点双连通子图。点双联通分量:无向图中的极大点双联通子图......
  • 【算法学习】圆方树——处理仙人掌的利器
    圆方树大概分两种,一个是圆方树,一个是广义圆方树。圆方树这可以解决仙人掌上的问题。任意一条边至多只出现在一条简单回路的无向连通图称为仙人掌。很多题解将其作为无向图构建,本文将其构建为外向树,在这个问题中两种构建方式不会影响求解。构建方式记读入的图为原图,构建的......
  • 圆方树
    圆方树这里的圆方树指广义圆方树。对于一张\(n\)个点的无向图,其中包含\(k\)个点双,那么这张图建出的圆方树一共有\(n+k\)个点,其中前\(n\)个点为原图中的点,称为圆点,后\(k\)个点每个点代表一个点双,称为方点,每个点双与其中包含的点连边构成一个菊花,这\(k\)个菊花经由图......
  • 圆方树学习笔记
    圆方树学习笔记圆方树是优秀的图论算法,从仙人掌图向无向图扩展,利用割点和点双联通分量的性质,实现了图向树的转换。对仙人掌的处理:圆方树——处理仙人掌的利器而且实现十分简单算法思路前置知识割点和桥,点双联通分量。思路对于一个无向图,圆方树理解可以如下:原图中点是圆......
  • 圆方树学习笔记
    今天在做ABC318G这道题,要用到圆方树的知识,于是就去学了圆方树。学习圆方树首先需要学习点双连通分量以及缩点,此处不多赘述。圆方树中分两种类型的点:圆点和方点。圆点指的是原来的无向图中的所有点,而方点指的是每一个点双连通分量所代表的点。相当于每一个点双连通分量就是一个......
  • 圆方树 useful things
    圆方树,是解决仙人掌问题的实用方法,假设最初图都是圆点,对于每个环新建一个方点并连接这个环上所有圆点,能很好规避同一个点可能属于很多个环的情况,并且发现build完之后是一棵树广义圆方树,能够不局限于去解决仙人掌问题,能上升到无向图层面,很好解决图上路径类,等等问题那么如何建立圆......
  • 圆方树 useful things
    圆方树,是解决仙人掌问题的实用方法,假设最初图都是圆点,对于每个环新建一个方点并连接这个环上所有圆点,能很好规避同一个点可能属于很多个环的情况,并且发现build完之后是一棵树广义圆方树,能够不局限于去解决仙人掌问题,能上升到无向图层面,很好解决图上路径类,等等问题那么如何建立圆......
  • 圆方树
    圆方树的引入我们知道,图没有很好的性质,而树有很多性质,并且容易通过很多方式来维护树上信息,因此将图上问题转化为树上问题是我们想要解决的。圆方树就是将图转化为树的数据结构。圆方树的分类圆方树分为两类:狭义圆方树,广义圆方树。狭义圆方树狭义圆方树是可以用来将仙人掌图转......