fzu 2082 過路費(樹鏈剖分,詢問兩點距離)

Problem 2082 過路費

Accept: 158    Submit: 630
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

有n座城市,由n-1條路相連通,使得任意兩座城市之間可達。每條路有過路費,要交過路費才能通過。每條路的過路費經常會更新,現問你,當前情況下,從城市a到城市b最少要花多少過路費。

Input

有多組樣例,每組樣例第一行輸入兩個正整數n,m(2 <= n<=50000,1<=m <= 50000),接下來n-1行,每行3個正整數a b c,(1 <= a,b <= n , a != b , 1 <= c <= 1000000000).數據保證給的路使得任意兩座城市互相可達。接下來輸入m行,表示m個操作,操作有兩種:一. 0 a b,表示更新第a條路的過路費爲b,1 <= a <= n-1 ; 二. 1 a b , 表示詢問a到b最少要花多少過路費。

Output

對於每個詢問,輸出一行,表示最少要花的過路費。

Sample Input

2 31 2 11 1 20 1 21 2 1

Sample Output

12
 
 
思路:樹鏈剖分,線段樹維護區間和
 
AC代碼:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;

const int INF = 1e9;
const int maxn = 100005;

struct Edge
{
    int v, next;
} et[maxn * 2];
ll tree[maxn * 4];
int n, m, z, num, root;
int d[maxn][2];
ll e[maxn];
int eh[maxn], dep[maxn], w[maxn], fa[maxn], top[maxn], son[maxn], siz[maxn];
void init()
{
    root = (n + 1) / 2;
    fa[root] = z = dep[root] = num = 0;
    memset(siz, 0, sizeof(siz));
    memset(tree, 0, sizeof(tree));
    memset(eh, -1, sizeof(eh));
}
void add(int u, int v)
{
    Edge e = {v, eh[u]};
    et[num] = e;
    eh[u] = num++;
}
void dfs(int u)
{
    siz[u] = 1;
    son[u] = 0;
    for(int i = eh[u]; i != -1; i = et[i].next)
    {
        int v = et[i].v;
        if(v == fa[u]) continue;
        fa[v] = u;
        dep[v] = dep[u] + 1;
        dfs(v);
        if(siz[v] > siz[son[u]]) son[u] = v;
        siz[u] += siz[v];
    }
}
void build_tree(int u, int tp)
{
    w[u] = ++z;
    top[u] = tp;
    if(son[u]) build_tree(son[u], top[u]);
    for(int i = eh[u]; i != -1; i = et[i].next)
    {
        int v = et[i].v;
        if(v != son[u] && v != fa[u]) build_tree(v, v);
    }
}
void update(int rt, int l, int r, int loc, ll x)
{
    if(loc > r || l > loc) return;
    if(l == r)
    {
        tree[rt] += x;
        return;
    }
    int mid = (l + r) >> 1, ls = rt * 2, rs = ls + 1;
    update(ls, l, mid, loc, x);
    update(rs, mid + 1, r, loc, x);
    tree[rt] = tree[ls] + tree[rs];
}
ll sum(int rt, int left, int right, int l, int r)
{
    if(l > right || r < left) return 0;
    if(l <= left && right <= r) return tree[rt];
    int mid = (left + right) >> 1, ls = rt * 2, rs = ls + 1;
    return sum(ls, left, mid, l, r) + sum(rs, mid + 1, right, l, r);
}
inline ll find(int u, int v)
{
    int f1 = top[u], f2 = top[v];
    ll tmp = 0;
    while(f1 != f2)
    {
        if(dep[f1] < dep[f2])
        {
            swap(f1, f2);
            swap(u, v);
        }
        tmp += sum(1, 1, z, w[f1], w[u]);
        u = fa[f1];
        f1 = top[u];
    }
    if(u == v) return tmp;
    if(dep[u] > dep[v]) swap(u, v);
    return tmp + sum(1, 1, z, w[son[u]], w[v]);
}
int main()
{
    int a, b, op;
    ll c;
    while(~scanf("%d%d", &n, &m))
    {
        init();
        for(int i = 0; i < n - 1; i++)
        {
            scanf("%d%d%I64d", &a, &b, &c);
            add(a, b);
            add(b, a);
            d[i][0] = a, d[i][1] = b, e[i] = c;
        }
        dfs(root);
        build_tree(root, root);
        for(int i = 0; i < n - 1; i++)
        {
            if(dep[d[i][0]] > dep[d[i][1]]) swap(d[i][0], d[i][1]);
            update(1, 1, z, w[d[i][1]], e[i]);
        }
        while(m--)
        {
            scanf("%d", &op);
            if(!op)
            {
                scanf("%d%I64d", &a, &c);
                update(1, 1, z, w[d[a - 1][1]], c - e[a - 1]);
                e[a - 1] = c;
            }
            else
            {
                scanf("%d%d", &a, &b);
                printf("%I64d\n", find(a, b));
            }
        }
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章