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

9 篇文章 0 订阅
7 篇文章 0 订阅

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 nodes and N edges, and you have to finish Q missions. Each mission consists of 3 integers OP, X and Y. 
When OP=0, you need to modify the weight of the Xth edge to Y. 
When OP=1, you need to calculate the length of the shortest path from node X to node Y.

Input

The first line contains a single integer T, the number of test cases. 
Each test case starts with a line containing two integers N and Q, the number of nodes (and edges) and the number of queries. (3≤N≤105)(1≤Q≤105) 
Each of the following N lines contain the description of the edges. The ith line represents the ith edge, which contains 3 space-separated integers ui, vi, and wi. This means that there is an undirected edge between nodes ui and vi, with a weight of wi. (1≤ui,vi≤N)(1≤wi≤105) 
Then Q lines follow, the ith line contains 3 integers OP, X and Y. The meaning has been described above.(0≤OP≤1)(1≤X≤105)(1≤Y≤105) 
It is guaranteed that the graph contains no self loops or multiple edges.

Output

For each test case, and for each mission whose OP=1, print one line containing one integer, the length of the shortest path between X and Y.



题意

给你一个基环树,和q次操作,每次询问x到y的最短路,或者修改一个边权。

题解

假如是个树,那么就是个树链剖分模板题,SPOJ - QTREE

现在只是多了个环,那么我们先拆掉环上一条边,然后做一个树链剖分。

对于在环上的边,我们用树状数组维护前缀,对于树里面的边我们用线段树维护区间和。

查询的时候如果x,y两点都在同一个环树上,直接用树链剖分查询即可。

如果不在一个环树,分成3个部分:x到他的根fx的距离,y到他的根fy的距离,以及环上fx到fy的距离。

环上两点的距离要么是 abs(sum(fx) - sum(fy)) 要么是 L - abs(sum(fx) - sum(fy)),L是环的长度,取min即可。

代码

#include <bits/stdc++.h>
#pragma comment(linker, "/STACK:102400000,102400000")

#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;

const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 1e5 + 5;

int n, q;
int ban;
struct Edge {
    int to, nxt, id;
} edge[MAXN * 2];
int head[MAXN], tot;
int top[MAXN];//top[v] 表示 v 所在的重链的顶端节点
int fa[MAXN];//父亲节点
int deep[MAXN];//深度
int num[MAXN];//num[v] 表示以 v 为根的子树的节点数
int p[MAXN];//p[v]表示 v 对应的位置
int fp[MAXN];//fp 和 p 数组相反
int son[MAXN];//重儿子
int pos;

ll fen[MAXN];
ll tree[MAXN << 2];

void update (int id, int l, int r, int x, int v) {
    if (l == r) {
        tree[id] = v;
        return ;
    }
    int mid = (l + r) >> 1;
    if (x <= mid) update (lson, x, v);
    else update (rson, x, v);
    tree[id] = tree[id << 1] + tree[id << 1 | 1];
}

ll query (int id, int l, int r, int L, int R) {
    if (L <= l && R >= r) return tree[id];
    int mid = (l + r) >> 1;
    ll ret = 0;
    if (L <= mid) ret += query (lson, L, R);
    if (R > mid) ret += query (rson, L, R);
    return ret;
}

void addedge (int u, int v, int i) {
    edge[tot].to = v;
    edge[tot].nxt = head[u];
    edge[tot].id = i;
    head[u] = tot++;
}
void dfs1 (int u, int pre, int d) {
    deep[u] = d;
    fa[u] = pre;
    num[u] = 1;
    for (int i = head[u]; i != -1; i = edge[i].nxt) {
        if (edge[i].id == ban) continue;
        int v = edge[i].to;
        if (v != pre) {
            dfs1 (v, u, d + 1);
            num[u] += num[v];
            if (son[u] == -1 || num[v] > num[son[u]])
                son[u] = v;
        }
    }
}

void getpos (int u, int sp) {
    top[u] = sp;
    p[u] = pos++;
    fp[p[u]] = u;
    if (son[u] == -1) return;
    getpos (son[u], sp);
    for (int i = head[u]; i != -1; i = edge[i].nxt) {
        if (edge[i].id == ban) continue;
        int v = edge[i].to;
        if ( v != son[u] && v != fa[u])
            getpos (v, v);
    }
}

ll getPath (int u, int v) {
    int f1 = top[u], f2 = top[v];
    ll tmp = 0;
    while (f1 != f2) {
        if (deep[f1] < deep[f2]) {
            swap (f1, f2);
            swap (u, v);
        }
        assert (p[f1] <= p[u]);
        tmp += query (1, 1, n, p[f1], p[u]);
        u = fa[f1];
        f1 = top[u];
    }
    if (u == v) return tmp;
    if (deep[u] > deep[v]) swap (u, v);
    assert (p[son[u]] <= p[v]);
    tmp += query (1, 1, n, p[son[u]], p[v]);
    return tmp;
}

ll sum (int i) {
    ll ret = 0;
    while (i) {
        ret += fen[i];
        i -= lowbit (i);
    }
    return ret;
}

void add (int i, int v) {
    while (i <= n + 1) {
        fen[i] += v;
        i += lowbit (i);
    }
}

int e[MAXN][3], in[MAXN];
int rt;
int noncyc[MAXN];
int cp[MAXN];

void init (int n) {
    tot = rt = 0;
    memset (head, -1,  sizeof (head) );
    pos = 1;//使用树状数组,编号从头 1 开始
    memset (son, -1, sizeof son );
    memset (fen, 0, sizeof fen );
    memset (tree, 0, sizeof tree );
    memset (in, 0, sizeof in );
    memset (noncyc, 0, sizeof noncyc );
    memset (cp, 0, sizeof cp );
}

void top_sort() {
    queue<int> q;
    for (int i = 1; i <= n; i++) if (in[i] == 1) q.push (i);
    while (!q.empty() ) {
        int now = q.front();
        q.pop();
        for (int i = head[now]; ~i; i = edge[i].nxt) {
            int v = edge[i].to;
            in[v]--;
            if (in[v] == 1) q.push (v);
        }
    }
    for (int i = 1; i <= n; i++) if (in[i] > 1) {
            rt = i;
            noncyc[i] = 1;
        }
    for (int i = head[rt]; ~i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (noncyc[v]) {
            ban = edge[i].id;
            break;
        }
    }
}

int dfs (int now) {
    if (noncyc[now]) return cp[now] = now;
    if (cp[now]) return cp[now];
    for (int i = head[now]; ~i; i = edge[i].nxt) {
        if(edge[i].id == ban) continue;
        int v = edge[i].to;
        if (deep[v] >= deep[now]) continue;
        return cp[now] = dfs (v);
    }
}


int main() {

    int T;
    cin >> T;
    while (T--) {
        scanf ("%d %d", &n, &q );
        init (n);
        for (int i = 1; i <= n; i++) {
            int u, v, w;
            scanf ("%d %d %d", &u, &v, &w);
            addedge (u, v, i);
            addedge (v, u, i);
            in[v]++;
            in[u]++;
            e[i][0] = u, e[i][1] = v, e[i][2] = w;
        }
        top_sort();
        dfs1 (rt, 0, 0);
        getpos (rt, rt);
        ll cyc = 0;
        for (int i = 1; i <= n; i++) {
            if (deep[e[i][0]] > deep[e[i][1]]) swap (e[i][0], e[i][1]);
            if (i != ban) {
                update (1, 1, n, p[e[i][1]], e[i][2]);
                if (noncyc[e[i][0]] && noncyc[e[i][1]]) {
                    add (p[e[i][1]], e[i][2]);
                }
            }
            if (noncyc[e[i][0]] && noncyc[e[i][1]]) cyc += e[i][2];
        }
        for (int i = 1; i <= n; i++) cp[i] = dfs (i);
      //  printf("%lld\n", cyc);
        while (q--) {
            int op, x, y;
            scanf ("%d %d %d", &op, &x, &y);
            if (op == 0) {
                if (x == ban) {
                    cyc += y - e[x][2];
                } else {
                    update (1, 1, n, p[e[x][1]], y);
                    if (noncyc[e[x][0]] && noncyc[e[x][1]]) {
                        add (p[e[x][1]], - e[x][2]);
                        add (p[e[x][1]], y);
                        cyc += y - e[x][2];
                    }
                }
                e[x][2] = y;
            } else {
                if (cp[x] == cp[y]) printf ("%lld\n", getPath (x, y) );
                else {
                    ll ans = 0;
                    ans += getPath (x, cp[x]);
                    ans += getPath (y, cp[y]);
                    if (p[cp[x]] > p[cp[y]]) swap (x, y);
                    ll t = sum (p[cp[y]]) - sum (p[cp[x]]);
                    ans += min (t, cyc - t);
                    printf ("%lld\n", ans);
                }
            }
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值