• fzu 2082 过路费 (树链剖分+线段树 边权)


    Problem 2082 过路费

    Accept: 887    Submit: 2881
    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 3 1 2 1 1 1 2 0 1 2 1 2 1

     Sample Output

    1 2
     
    思路:
    基于边权进行树链剖分
    题目说了半天其实就是给你一棵树,修改边权,询问两点之间边的权值和(n个点,n-1条边,点两两互通,摆明了是树结构了),
    基于边权的树链剖分和基于点权的处理方面上有些不同。
    观察一棵树的结构我们可以看出树上每一个点都可以一一对应和他的父节点相连的边,那么我们就可以用这些点表示树上的边进行
    树链剖分。代码算非常裸的树链剖分+线段树区间和,只要注意下一些处理上的小细节就好了。
     
    实现代码:
    #include<iostream>
    #include<cstring>
    using namespace std;
    #define ll long long
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define mid ll m = (l + r) >> 1;
    const ll M = 1e5+10;
    ll sum[M<<2],son[M],siz[M],head[M],top[M],fa[M],dep[M],tid[M<<2],rk[M<<2];
    ll u[M],v[M],cnt1,cnt,wt[M],n,m;
    struct node{
        ll to,next,w;
    }e[M];
    
    void add(ll u,ll v,ll c){
        e[++cnt1].to=v;e[cnt1].next=head[u];e[cnt1].w=c;head[u]=cnt1;
        e[++cnt1].to=u;e[cnt1].next=head[v];e[cnt1].w=c;head[v]=cnt1;
    }
    void dfs1(ll u,ll faz,ll deep){
        dep[u] = deep;
        siz[u] = 1;
        fa[u] = faz;
        for(ll i = head[u];i ;i=e[i].next){
            ll v = e[i].to;
            if(v != fa[u]){
                wt[v] = e[i].w;
                dfs1(v,u,deep+1);
                siz[u] += siz[v];
                if(son[u]==-1||siz[v]>siz[son[u]])
                    son[u] = v;
            }
        }
    }
    
    void dfs2(ll u,ll t){
        top[u] = t;
        tid[u] = cnt;
        rk[cnt] = wt[u];
        //cout<<1<<endl;
        cnt++;
        if(son[u] == -1) return ;
        dfs2(son[u],t);
        for(ll i = head[u];i;i=e[i].next){
            ll v = e[i].to;
            if(v != son[u]&&v != fa[u])
                dfs2(v,v);
        }
    }
    
    void pushup(ll rt){
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    }
    
    void update(ll p,ll c,ll l,ll r,ll rt){
        if(l == r){
            sum[rt] = c;
            return;
        }
        mid;
        if(p <= m) update(p,c,lson);
        else update(p,c,rson);
        pushup(rt);
    }
    
    void build(ll l,ll r,ll rt){
        if(l == r){
            sum[rt] = rk[l];
            return ;
        }
        mid;
        build(lson);
        build(rson);
        pushup(rt);
    }
    
    ll query(ll L,ll R,ll l,ll r,ll rt){
        if(L <= l&&R >= r){
            return sum[rt];
        }
        mid;
        ll ret = 0;
        if(L <= m) ret += query(L,R,lson);
        if(R > m) ret += query(L,R,rson);
        return ret;
    }
    
    ll ask(ll x,ll y){
        ll ans = 0;
        ll fx = top[x],fy = top[y];
        while(fx != fy){
            if(dep[fx] < dep[fy]) swap(fx,fy),swap(x,y);
            if(fx == 1) ans += query(tid[fx]+1,tid[x],1,n,1);
            else ans += query(tid[fx],tid[x],1,n,1);
            x = fa[fx]; fx = top[x];
        }
        //当x,y top相同时,因为当前这个点代表的是它与父节点之前的边的权值
        //top相同时有两种情况:1。两点重合,2.两点在同一重链上
        if(x==y) return ans;  //两点重合时直接返回当前的值就好了。
        if(dep[x] > dep[y]) swap(x,y);
        ans += query(tid[x]+1,tid[y],1,n,1); //当两点在同一重链上时,dep较小的边需要+1。
        return ans;
    }
    
    void init()
    {
        memset(son,-1,sizeof(son));
        for(ll i = 0; i <= n;i ++){
            e[i].to = 0;e[i].w = 0;e[i].next = 0;head[i] = 0;
        }
    }
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        ll c,x,a,b;
        while(cin>>n>>m){
        init();
        cnt1 = 1;cnt = 1;
        for(ll i = 1;i < n;i ++){
            cin>>u[i]>>v[i]>>c;
            add(u[i],v[i],c);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        //cout<<1<<endl;
        build(1,n,1);
        while(m--){
           cin>>x>>a>>b;
           if(x==0){
               if(dep[u[a]] < dep[v[a]]) swap(u[a],v[a]);
               update(tid[u[a]],b,1,n,1);
           }
           else{
            cout<<ask(a,b)<<endl;
           }
        }
        }
        return 0;
    }
  • 相关阅读:
    每日一题_190921
    每日一题_190920
    每日一题_190919
    2017年江苏高考数学14题
    2017年全国高中数学联赛及2017年贵州省预赛试题
    高中数学与初中数学的接轨点
    从此错位(相减)无计算
    在 LaTeX 中同步缩放 TikZ 与其中的 node
    析构函数 一定是虚函数
    Java对多线程的支持
  • 原文地址:https://www.cnblogs.com/kls123/p/8922325.html
Copyright © 2020-2023  润新知