圆方树小结

圆方树

jzoj 1914. 【2011集训队出题】最短路
这是道圆方树+倍增LCA裸题。
圆方树,顾名思义,就是圆点和方点所组成的树。
而方点就是一个圆的根,一般都是\(dfs\)时第一个到这个圆的那个位置,然后另附一个点当做方点。然后圆所组成的点都连向方点。
而对于这种圆方边的边权,则为它到根的最近值。
从而将一个仙人掌转成了一棵树。

然后对于这棵树,我们就可以用倍增来求出两两点之间的最短路了。
注意的是,对于最后走到的位置,我们要看看它是从上面绕近还是直接从下面走更优!!!
就这样子了。

\(code\)

#include <cstdio>
#include <algorithm>
#define N 100010
#define mem(x, a) memset(x, a, sizeof x)
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
using namespace std;
struct node{int v, fr, w;}e[N << 1];
struct edge{int v, fr;}g[N << 1];
int n, n1, m, Q, tail[N], cnt = 1, len[N];
int dfn[N], low[N], fa[N], tot = 0, ri[N];
int f[N][16], l[N][16], cir[N], fz[N], dep[N];
int head[N], cnt1 = 1;
int z[N], top = 0;

inline int read()
{
    int x = 0, f = 0; char c = getchar();
    while (c < '0' || c > '9') f = (c == '-') ? 1 : f, c = getchar();
    while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return f ? -x : x;
}

inline void add(int u, int v, int w) {e[++cnt] = (node){v, tail[u], w}; tail[u] = cnt;}

inline void add1(int u, int v) {g[++cnt1] = (edge){v, head[u]}; head[u] = cnt1;}

void tarjan(int x)
{
    dfn[x] = low[x] = ++tot, z[++top] = x;
    for (int p = tail[x], v; p; p = e[p].fr)
    {
        v = e[p].v;
        if (! dfn[v])
        {
            fa[v] = x, len[v] = e[p].w;
            tarjan(v);
            low[x] = min(low[x], low[v]);
            if (low[v] >= dfn[x])
            {
                cir[++n] = fz[z[top]], add1(x, n);
                int u = top;
                while (z[u] != x)
                    cir[n] += len[z[u]], add1(n, z[u]), u--;
                ri[z[u]] = 0, u++;
                while (u <= top) ri[z[u]] = ri[z[u - 1]] + len[z[u]], u++;
                while (z[top] != x) l[z[top]][0] = min(ri[z[top]], cir[n] - ri[z[top]]), top--;
            }
        }
        else if (dfn[v] < low[x])
            fz[x] = e[p].w, low[x] = dfn[v];
    }
}

void get_dep(int x)
{
    for (int p = head[x], v; p; p = g[p].fr)
        v = g[p].v, f[v][0] = x, dep[v] = dep[x] + 1, get_dep(v);
}

int LCA(int x, int y)
{
    int ans = 0;
    if (dep[x] < dep[y]) swap(x, y);
    for (int i = 0, cha = dep[x] - dep[y]; cha; i++, cha >>= 1)
        if (cha & 1) ans += l[x][i], x = f[x][i];
    if (x == y) return ans;
    fd(i, 14, 0)
        if (f[x][i] != f[y][i])
        {
            ans += l[x][i] + l[y][i];
            x = f[x][i], y = f[y][i];
        }
    if (cir[f[x][0]]) return ans + min(abs(ri[x] - ri[y]), cir[f[x][0]] - abs(ri[x] - ri[y]));
    else return ans + l[x][0] + l[y][0];
}

int main()
{
    n = n1 = read(), m = read(), Q = read();
    fo(i, 1, m)
    {
        int u = read(), v = read(), w = read();
        add(u, v, w), add(v, u, w);
    }
    tarjan(1), get_dep(1);
    fo(j, 1, 14)
        fo(i, 1, n)
        {
            f[i][j] = f[f[i][j - 1]][j - 1];
            l[i][j] = l[i][j - 1] + l[f[i][j - 1]][j - 1];
        }
    fo(i, 1, Q)
    {
        int u = read(), v = read();
        printf("%d\n", LCA(u, v));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/jz929/p/12168579.html

时间: 2024-10-09 18:21:55

圆方树小结的相关文章

圆方树学习

圆方树是一种数据结构. 这个东西原始的出处应该是paper <Maintaining bridge-connected and biconnected components on-line> tarjan和另外一个人写的...当时叫forest data structure 然后这个东西似乎已经流行很久了?http://blog.csdn.net/PoPoQQQ/article/details/49513819 cjk大爷最近发了一篇博客写这个:http://immortalco.blog.u

【BZOJ】2125: 最短路 圆方树(静态仙人掌)

[题意]给定带边权仙人掌图,Q次询问两点间最短距离.n,m,Q<=10000 [算法]圆方树处理仙人掌问题 [题解]树上的两点间最短路问题,常用倍增求LCA解决,考虑扩展到仙人掌图. 先对仙人掌图建圆方树,圆圆边和原图边权一致.对于每个方点代表的环,记深度最小的点为x,则圆方边的边权是圆点到x的最短距离. 若lca(u,v)为圆点,则两点间最短路转化为圆方树上dis[u]+dis[v]-2*dis[lca].(向上延伸的路径,经过环则必然经过每个方点的x,计算无误) 若lca(u,v)为方点,则

CF487E Tourists 【圆方树 + 树剖 + 堆】

题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用了一对一的特性优化了复杂度 如此询问时如果\(lca\)为方点,再询问一下\(lca\)的父亲即可 复杂度\(O(qlog^2n)\) #include<algorithm> #include<iostream> #include<cstring> #include<

【P4320】 道路相遇 (圆方树+LCA)

题目链接 题意:给一张无向图和\(M\)个询问,问\(u,v\)之间的路径的必经之点的个数. 对图建出圆方树,然后必经之点就是两点路径经过的原点个数,用\((dep[u]+dep[v]-dep[LCA]*2)/2+1\)即可算出. 什么你不知道圆方树(说的跟我知道一样) \(APIO2018\)出来的黑科技,详见\(APIO2018\)铁人两项. 就是对每个点双新建一个点,然后让点双里所有点都对这个点连边. 看图. #include <cstdio> const int MAXN = 5000

Tourists——圆方树

CF487E Tourists 一般图,带修求所有简单路径代价. 简单路径,不能经过同一个点两次,那么每个V-DCC出去就不能再回来了. 所以可以圆方树,然后方点维护一下V-DCC内的最小值. 那么,从任意一个割点进入这个DCC,必然可以绕一圈再从另一个割点出去. 所以,路径上的最小值,就是圆方树路径上的最小值.方点的最小值就是在这个DCC中走一走得到的. 树链剖分+线段树维护路径 用堆维护方点四周的圆点的最小值.然后更新. 一个问题是: 更新一个割点圆点,会影响到四周所有的方点.暴力更新,菊花

CF487E Tourists 圆方树、树链剖分

传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就是一个反例 上面这个图如果缩点双会缩成\(3\)个,但是缩边双会将整个图缩成\(1\)个点. 假如我们询问的是\((1,4)\)之间的简单路径,而图中权值最小的点为\(7\)号点,那么如果缩成了边双联通分量,你的答案会是\(7\)号点的权值,意即认为可以走到\(7\)号点,但实际上如果到\(7\)号

道路相遇 圆方树

道路相遇 基础圆方树. 不会圆方树看我另一篇文章 LG传送门 发现必经之路上的点一定是简单路径上的点,可以自己手玩.处理无向图上的简单路径,考虑把圆方树建出来,发现答案就是园方树上两点间圆点个数.由于广义园方树上圆方点相间,可以用深度表示答案,发现答案就是\((dep[u] + dep[v] - 2 * dep[lca]) / 2 + 1\). #include <cstdio> #include <cctype> #include <vector> #define R

[SDOI2018]战略游戏 圆方树,树链剖分

[SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中的圆点个数减去\(S\).问题变成了怎样求这样的连通块中的圆点个数,直接给结论吧:先搞出树的dfs序,把询问的点按dfs序从小到大排一遍序,每次把答案加上第\(i\)和第\(i + 1\)个点之间的圆点个数,但是不算lca,再加上第\(1\)个和第\(S\)个点之间的圆点个数,然后除以二就得到了这个

[圆方树] Luogu P4630 Duathlon 铁人两项

题目描述 比特镇的路网由 mm 条双向道路连接的 nn 个交叉路口组成. 最近,比特镇获得了一场铁人两项锦标赛的主办权.这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程. 比赛的路线要按照如下方法规划: 先选择三个两两互不相同的路口 s, cs,c和 ff,分别作为比赛的起点.切换点(运动员在长跑到达这个点后,骑自行车前往终点).终点. 选择一条从 ss出发,经过 cc最终到达 ff的路径.考虑到安全因素,选择的路径经过同一个点至多一次. 在规划路径之前,镇长想请你帮忙计