codeforces838B Diverging Directions -- 树链剖分 + 线段树

ai 表示从 i 到根的边的长度。对于每个点维护 disti 表示从根到 i 的路径, mindisti 表示 i 的子树中 distj+aj 的最小值。
然后对于询问 (u,v) 分类:
如果 u v 的祖先,由于权值都是正整数,答案为 distvdistu
否则答案为 mindistudistu+distv
dist mindist dfs 序+线段树维护就可以了。

代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    if(p1==p2){
        p2=(p1=buf)+fread(buf,1,100000,stdin);
        if(p1==p2)return EOF;
    }
    return *p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
#define N 200010
#define ll long long
struct Edge{
    int f,t,nx,w;
}e[N<<1];
ll c[N<<2],p[N<<2],Pa[N],c2[N<<2];
int d[N],Top[N],Son[N],l[N],r[N],s[N],f[N],w[N],x,y,z;
int h[N],Num;
int i,j,k,n,m,Q,a[N],L;
inline void Dfs1(int x,int y){
    d[x]=++y;l[x]=++Num;s[x]=1;w[Num]=x;
    for(int i=h[x];i;i=e[i].nx){
        Pa[e[i].t]=Pa[x]+e[i].w;
        Dfs1(e[i].t,y);
        s[x]+=s[e[i].t];
        if(s[e[i].t]>s[Son[x]])Son[x]=e[i].t;
    }
    r[x]=Num;
}
inline void Dfs2(int x,int y){
    Top[x]=y;
    if(Son[x])Dfs2(Son[x],y);
    for(int i=h[x];i;i=e[i].nx)
    if(e[i].t!=Son[x])Dfs2(e[i].t,e[i].t);
}
inline ll Min(ll x,ll y){
    return x<y?x:y;
}
inline void Up(int x){
    c[x]=Min(c[x<<1],c[x<<1|1]);
}
inline void Down(int x){
    if(p[x]){
        c[x<<1]+=p[x];c2[x<<1]+=p[x];p[x<<1]+=p[x];
        c[x<<1|1]+=p[x];c2[x<<1|1]+=p[x];p[x<<1|1]+=p[x];
        p[x]=0;
    }
}
inline void Update1(int x,int l,int r,int y,int z){
    if(l==r){
        c[x]+=z;
        return;
    }
    Down(x);
    int Mid=l+r>>1;
    if(y<=Mid)Update1(x<<1,l,Mid,y,z);else Update1(x<<1|1,Mid+1,r,y,z);
    Up(x);
}
inline void Update2(int x,int l,int r,int L,int R,int y){
    if(l>R||r<L)return;
    if(l>=L&&r<=R){
        c[x]+=y;c2[x]+=y;p[x]+=y;
        return;
    }
    Down(x);
    int Mid=l+r>>1;
    Update2(x<<1,l,Mid,L,R,y);Update2(x<<1|1,Mid+1,r,L,R,y);
    Up(x);
}
inline ll Query(int x,int l,int r,int L,int R){
    if(l>R||r<L)return 1e18;
    if(l>=L&&r<=R)return c[x];
    Down(x);
    int Mid=l+r>>1;
    return Min(Query(x<<1,l,Mid,L,R),Query(x<<1|1,Mid+1,r,L,R));
}
inline ll Query2(int x,int l,int r,int y){
    if(l==r)return c2[x];
    Down(x);
    int Mid=l+r>>1;
    if(y<=Mid)return Query2(x<<1,l,Mid,y);
    return Query2(x<<1|1,Mid+1,r,y);
}
inline void Build(int x,int l,int r){
    if(l==r){
        c[x]=Pa[w[l]]+a[w[l]];
        c2[x]=Pa[w[l]];
        return;
    }
    int Mid=l+r>>1;
    Build(x<<1,l,Mid);Build(x<<1|1,Mid+1,r);
    Up(x);
}
inline int Lca(int x,int y){
    while(Top[x]!=Top[y])
    if(d[Top[x]]>d[Top[y]])x=f[Top[x]];else y=f[Top[y]];
    return d[x]>d[y]?y:x;
}
int main(){
    Read(n);Read(Q);
    for(i=1;i<n;i++)Read(e[i].f),Read(e[i].t),Read(e[i].w),e[i].nx=h[e[i].f],h[e[i].f]=i,f[e[i].t]=e[i].f;
    for(i=1;i<n;i++)Read(e[i+n-1].f),Read(e[i+n-1].t),Read(a[e[i+n-1].f]);
    Num=0;Dfs1(1,0);Dfs2(1,1);
    Build(1,1,n);
    while(Q--){
        Read(k);Read(x);Read(y);
        if(k==1){
            if(x>=n)Update1(1,1,n,l[e[x].f],y-a[e[x].f]),a[e[x].f]=y;else
            Update2(1,1,n,l[e[x].t],r[e[x].t],y-e[x].w),e[x].w=y;
        }else{
            L=Lca(x,y);
            if(L==x)printf("%I64d\n",Query2(1,1,n,l[y])-Query2(1,1,n,l[x]));else
            printf("%I64d\n",Query(1,1,n,l[x],r[x])-Query2(1,1,n,l[x])+Query2(1,1,n,l[y]));
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值