H - Traffic Network in Numazu HDU - 6393(基环树)

Traffic Network in Numazu (HDU - 6393)

题意:给定一张\(n\)个点\(n\)条边的带权图。要求支持两种操作:

  • \(0\ x\ y :\)修改第\(x\)条边的权值为\(y\)。
  • \(1\ x\ y :\)查询\((x,y)\)的最短路。

题解:

\(n\)个点\(n\)条边,就是一颗基环树。我们可以拆掉基环树上的一条边,变为一棵树。那么两个点的最短路就是树上的距离和经过拆掉的边的距离,取最小值。

对于树上的距离,我们可以用线段树维护每个点到根节点的距离来求出。每次修改一条边的权值时,就把这条边以下的子树整体修改。

对于\((x,y)\)经过被拆掉的边\((u,v,len)\)的情况,我们可以在\((x, u)+(y, v)+len\)与\((x,v)+(y,u)+len\)取\(min\)。

最后把上面两种情况取\(min\)就是询问的答案。

代码:

#include <bits/stdc++.h>
#define fopi freopen("in.txt", "r", stdin)
#define fopo freopen("out.txt", "w", stdout)
using namespace std;
typedef long long LL;
typedef pair<int, LL> Pair;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 10;

LL d[maxn];
struct SegTree {
    struct Node {
        int l, r;
        LL sum, add;
    }t[maxn*4];

    void build(int id, int l, int r) {
        t[id].l = l, t[id].r = r;
        t[id].add = 0;
        if (l == r) {
            t[id].sum = d[t[id].l];
            return;
        }
        int mid = (l+r) / 2;
        build(id*2, l, mid);
        build(id*2+1, mid+1, r);
        t[id].sum = t[id*2].sum + t[id*2+1].sum;
    }

    void pushdown(int id) {
        if (t[id].add != 0) {
            t[id*2].add += t[id].add;
            t[id*2+1].add += t[id].add;
            int mid = (t[id].l + t[id].r) / 2;
            t[id*2].sum += t[id].add * (mid-t[id].l+1);
            t[id*2+1].sum += t[id].add * (t[id].r-mid);
            t[id].add = 0;
        }
    }

    void update(int id, int l, int r, LL val) {
        if (l <= t[id].l && r >= t[id].r) {
            t[id].add += val;
            t[id].sum += val * (t[id].r-t[id].l+1);
            return;
        }
        pushdown(id);
        int mid = (t[id].l + t[id].r) / 2;
        if (r <= mid) update(id*2, l, r, val);
        else if (l > mid) update(id*2+1, l, r, val);
        else update(id*2, l, mid, val), update(id*2+1, mid+1, r, val);
        t[id].sum = t[id*2].sum + t[id*2+1].sum;
    }

    LL query(int id, int x) {
        if (t[id].l == x && t[id].r == x) return t[id].sum;
        pushdown(id);
        int mid = (t[id].l + t[id].r) / 2;
        if (x <= mid) query(id*2, x); else query(id*2+1, x);
    }
}ST;

int fa[maxn][22], dep[maxn], dfn[maxn], dfr[maxn];
vector<Pair> V[maxn];
int depth, tot;

void init_lca(int x, int from) {
    dfn[x] = ++tot;
    dep[x] = dep[from] + 1;
    for (auto p : V[x]) {
        int y = p.first, l = p.second;
        if (y == from) continue;
        fa[y][0] = x;
        for (int j = 1; j <= depth; j++)
            fa[y][j] = fa[fa[y][j-1]][j-1];
        init_lca(y, x);
    }
    dfr[x] = tot;
}

void dfs(int x, int from) {
    for (auto p : V[x]) {
        int y = p.first, l = p.second;
        if (y == from) continue;
        d[dfn[y]] = d[dfn[x]] + l;
        dfs(y, x);
    }
}

int lca(int x, int y) {
    if (dep[x] > dep[y]) swap(x, y);
    for (int i = depth; i >= 0; i--)
        if (dep[fa[y][i]] >= dep[x]) y = fa[y][i];
    if (x == y) return x;
    for (int i = depth; i >= 0; i--)
        if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

LL dist(int x, int y) {
    int L = lca(x, y);
    LL d1 = ST.query(1, dfn[x]), d2 = ST.query(1, dfn[y]), d3 = ST.query(1, dfn[L]);
    return d1 + d2 - 2 * d3;
}

int T, n, m;
LL z[maxn];
int y[maxn];
int main() {
    scanf("%d", &T);
    for (int ca = 1; ca <= T; ca++) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) V[i].clear();

        for (int i = 1; i <= n-1; i++) {
            int x;
            scanf("%d%d%d", &x, &y[i], &z[i]);
            //其实这里应该按照dep的深度存第i条边的儿子节点。
            //所幸题目中没有逆序边,我也没wa。
            V[x].push_back({y[i], z[i]});
            V[y[i]].push_back({x, z[i]});
        }
        int xn, yn;
        scanf("%d%d%d", &xn, &yn, &z[n]);

        depth = 20, tot = 0;
        init_lca(1, 0);
        dfs(1, 0);
        ST.build(1, 1, n);
        for (int i = 1; i <= m; i++) {
            int op, x, val;
            scanf("%d%d%d", &op, &x, &val);
            if (op == 0) {
                if (x == n) { z[n] = val; continue; }
                LL deta = val - z[x];
                ST.update(1, dfn[y[x]], dfr[y[x]], deta);
                z[x] = val;
            }
            else {
                int fx = dfn[x], fy = dfn[val];
                LL d1 = dist(x, val),
                    d2 = dist(x, xn) + dist(val, yn) + z[n],
                    d3 = dist(x, yn) + dist(val, xn) + z[n];
                printf("%lld\n", min(d1, min(d2, d3)));
            }
        }
    }
}

原文地址:https://www.cnblogs.com/ruthank/p/11369306.html

时间: 2024-10-14 10:10:19

H - Traffic Network in Numazu HDU - 6393(基环树)的相关文章

Traffic Network in Numazu

Traffic Network in Numazu 题目描述 Chika is elected mayor of Numazu. She needs to manage the traffic in this city. To manage the traffic is too hard for her. So she needs your help. You are given the map of the city -- an undirected connected weighted gr

杭电多校第七场 Traffic Network in Numazu

Problem Description Chika is elected mayor of Numazu. She needs to manage the traffic in this city. To manage the traffic is too hard for her. So she needs your help. You are given the map of the city -- an undirected connected weighted graph with N

[HDU 5304] 基环树计数

题意 给定一张 n 个点 m 条边的无向图, 问其有多少个生成基环树. n <= 16 , 无重边, 无自环. 分析 状压DP 处理每个状态对应的环的个数. 枚举所有状态, 缩点后重标号, 使用 Matrix 定理计算答案. 实现 枚举环: f[s][i] 表示从 s 状态中的最小值出发, 终点在 i 的方案数. 边界 f[2 ^ i][i] = 1 . 统计答案的时候, 对于 f[s][i] , 若 s 中的个数大于 2 , 且 i 能到达 s 状态中的最小值, 则 cnt[s] += f[s

HDU - 6393 Traffic Network in Numazu(树链剖分+基环树)

http://acm.hdu.edu.cn/showproblem.php?pid=6393 题意 给n个点和n条边的图,有两种操作,一种修改边权,另一种查询u到v的最短路. 分析 n个点和n条边,实际上是一棵树+一个环,如果仅仅是一棵树,那么这题就是树链剖分的模板题了. 对于环来说,可以考虑先把环中一条边提取出来,然后树链剖分,修改时用线段树,单点修改和区间求和. 查询时就考虑三种情况,u走树上到v,从u经过提取出来的边再到v(两种),取最小值. 至于取出环上一条边,用并查集搞搞. #incl

HDU - 6393 Traffic Network in Numazu (LCA+RMQ+树状数组)

这道题相当于将这两题结合: http://poj.org/problem?id=2763 http://codeforces.com/gym/101808/problem/K 题意:有N各点N条边的带权无向图(相当于一棵树多了一条边),两种操作:修改一条边的权值:求两点间的最短路径. 分析:将任意一条边取出来,其余n-1条边可以结合LCA解最短路.询问时,比较通过取出的边和仅通过树上的边的路径的大小,最小值就是两点的最短路径. 树状数组差分维护点到根节点的距离,根据dfs序来记录需要维护的范围.

HDU contest808 ACM多校第7场 Problem - 1008: Traffic Network in Numazu

首先嘚瑟一下这场比赛的排名:59 (第一次看到这么多 √ emmmm) 好了进入正文QAQ ...这道题啊,思路很清晰啊. 首先你看到树上路径边权和,然后还带修改,不是显然可以想到 树剖+线段树 维护重链么? 然后你再看啊,这是一个连通图,然后有 n 个点 n 条边,于是很显然会有一个环(然后就构成了一个 仙人掌 ...不过我并不了解仙人掌) 然后你再看!这里只会有一个环,我们假设没有这个环,那么这就是一道 树剖 模板题,那么我们可不可以特殊地,让这个环当根,除这个环以外的其他节点来简单 树剖

Method for finding shortest path to destination in traffic network using Dijkstra algorithm or Floyd-warshall algorithm

A method is presented for finding a shortest path from a starting place to a destination place in a traffic network including one or more turn restrictions, one or more U-turns and one or more P-turns using a Dijkstra algorithm. The method as sets a

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

HDU 4417 划分树+二分

题意:有n个数,m个询问(l,r,k),问在区间[l,r] 有多少个数小于等于k. 划分树--查找区间第k大的数.... 利用划分树的性质,二分查找在区间[l,r]小于等于k的个数. 如果在区间第 i 大的数tmp>k,则往下找,如果tmp<k,往上找. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #incl