P3976 [TJOI2015] 旅游

题意

给一棵 \(n\) 个点的树,点有点权,点 \(i\) 的点权记作 \(a_i\)\(q\) 次询问,每次询问给出 \((u,v,w)\),表示查询 \(u\rightarrow v\) 的唯一简单路径上的两个点 \(p_1,p_2\),使得 \(a_{p_2}-a_{p_1}\) 最大,且在 \(u\rightarrow v\) 的唯一简单路径中 \(p_1\)\(p_2\) 前面,输出最大的 \(a_{p_2}-a_{p_1}\) 并给 \(u\rightarrow v\) 的唯一简单路径上的所有点点权加 \(w\)
\(n,q\le5\times10^4\),任意时刻 \(a_u\le10^9\)

思路

用树剖。线段树上每个节点记录子区间内的答案(正反都要),子区间最值和懒标记。设 \(ord,rev\) 表示正着的答案和反着的答案,\(tmin,tmax\) 表示区间最值。合并:

\[\begin{array}{lr} ord_u\leftarrow \max(ord_{ls(u)},ord_{rs(u)},tmax_{rs(u)}-tmin_{ls(u)})\\ rev_u\leftarrow \max(rev_{ls(u)},rev_{rs(u)},tmax_{ls(u)}-tmin_{rs(u)})\\ \end{array} \]

修改直接做,查询时利用 \(ord\)\(rev\) 算出答案。

代码

有点长。

// Problem: P3976 [TJOI2015] 旅游
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3976
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=50010;
int n,q,a[maxn],aa[maxn];
vector<int>e[maxn];
struct node{
    int tag,min,max,ord,rev;
    node(){tag=0,min=1000000000,max=ord=rev=0;}
};
node merge(node a,node b){
    node ans;
    ans.min=min(a.min,b.min),ans.max=max(a.max,b.max);
    ans.ord=max({a.ord,b.ord,b.max-a.min});
    ans.rev=max({a.rev,b.rev,a.max-b.min});
    return ans;
}
class Segment_Tree{
private:
    node t[maxn*4];
    void add_tag(int u,int z){t[u].tag+=z,t[u].min+=z,t[u].max+=z;}
    void down(int u){
        add_tag(u<<1,t[u].tag),add_tag(u<<1|1,t[u].tag);
        t[u].tag=0;
    }
    void update(int u,int l,int r,int ll,int rr,int z){
        if(l>rr||r<ll) return ;
        if(ll<=l&&r<=rr){add_tag(u,z);return ;}
        down(u);
        int mid=l+r>>1;
        update(u<<1,l,mid,ll,rr,z),update(u<<1|1,mid+1,r,ll,rr,z);
        t[u]=merge(t[u<<1],t[u<<1|1]);
    }
    node query(int u,int l,int r,int ll,int rr){
        if(ll<=l&&r<=rr) return t[u];
        int mid=l+r>>1;
        down(u);
        if(mid>=rr) return query(u<<1,l,mid,ll,rr);
        if(mid<ll) return query(u<<1|1,mid+1,r,ll,rr);
        return merge(query(u<<1,l,mid,ll,rr),query(u<<1|1,mid+1,r,ll,rr));
    }
public:
    void update(int l,int r,int z){update(1,1,n,l,r,z);}
    node query(int l,int r){return query(1,1,n,l,r);}
    void build(int u=1,int l=1,int r=n){
        if(l==r){t[u].min=t[u].max=aa[l];return ;}
        int mid=l+r>>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);
        t[u]=merge(t[u<<1],t[u<<1|1]);
    }
}t;
namespace HLD{
    int fa[maxn],top[maxn],deep[maxn],son[maxn],sz[maxn],dfn[maxn],cnt_dfn;
    void dfs1(int u){
        sz[u]=1;
        deep[u]=deep[fa[u]]+1;
        for(int v:e[u]){
            if(v==fa[u]) continue;
            fa[v]=u;
            dfs1(v);
            if(sz[v]>sz[son[u]]) son[u]=v;
            sz[u]+=sz[v];
        }
    }
    void dfs2(int u,int tp){
        top[u]=tp;
        dfn[u]=++cnt_dfn;
        if(son[u]) dfs2(son[u],tp);
        for(int v:e[u]){
            if(v==fa[u]||v==son[u]) continue;
            dfs2(v,v);
        }
    }
    void build(){
        dfs1(1);
        dfs2(1,1);
        for(int i=1;i<=n;i++) aa[dfn[i]]=a[i];
        t.build();
    }
    void update(int u,int v,int w){
        while(top[u]!=top[v]){
            if(deep[top[u]]<deep[top[v]]) swap(u,v);
            t.update(dfn[top[u]],dfn[u],w);
            u=fa[top[u]];
        }
        if(dfn[u]<dfn[v]) swap(u,v);
        t.update(dfn[v],dfn[u],w);
    }
    int query(int u,int v){
        int ans=0;
        node nwu,nwv;
        while(top[u]!=top[v]){
            if(deep[top[u]]>deep[top[v]]){
                nwu=merge(t.query(dfn[top[u]],dfn[u]),nwu);
                u=fa[top[u]];
            }
            else{
                nwv=merge(t.query(dfn[top[v]],dfn[v]),nwv);
                v=fa[top[v]];
            }
        }
        if(deep[u]<deep[v]){
            nwv=merge(t.query(dfn[u],dfn[v]),nwv);
            swap(nwu.ord,nwu.rev);
            nwu=merge(nwu,nwv);
            return nwu.ord;
        }
        nwu=merge(t.query(dfn[v],dfn[u]),nwu);
        swap(nwu.ord,nwu.rev);
        nwu=merge(nwu,nwv);
        return nwu.ord;
    }
};
signed main(){
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<n;i++){
        int u,v;read(u,v);
        e[u].push_back(v),e[v].push_back(u);
    }
    HLD::build();
    read(q);
    while(q--){
        int a,b,v;read(a,b,v);
        write(HLD::query(a,b));write("\n");
        HLD::update(a,b,v);
    }
    return 0;
}
posted @ 2026-06-15 15:44  Link-Cut_Trees  阅读(10)  评论(0)    收藏  举报