把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

题解 P3976 [TJOI2015] 旅游

传送门

题意

有一棵 $n$ 个节点的树,每个节点有一个权值。
有 $q$ 此操作,每次操作由 $u$ 沿最短路径到达 $v$,求 $val_i-val_j$ 的最大值,($i$,$j$ 位于此最短路径上,且先经过 $i$,再经过 $j$),经过后,此路径上所有点的价值上升 $v$。

分析

作为一道树形并且是最短路径的题,我们当然先从序列上思考。

显然是用线段树来维护,重点又变成了区间上的合并。

先思考从左到右,我们合并后的总区间的答案,只有三种可能:

  1. 左区间买,左区间卖。
  2. 左区间买,右区间卖。
  3. 右区间买,右区间卖。

对于第一种与第三种情况,我们可以直接从左区间与右区间继承,作为第二种,显然是从左区间的最小值买,右区间的最大值卖,之后就可以维护好我们的区间合并。由于不止左至右,我们同时需要维护右到左。

我们使用结构体来维护我们的这个区间的左至右的答案,右至左的答案,区间最大值,区间最小值。

struct node {
    int mx,mn,c1,c2;//c1 左至右  c2 右至左
    inline friend node operator + (node fi,node se) {
        node res;
        res.mx=max(fi.mx,se.mx);
        res.mn=min(fi.mn,se.mn);
        res.c1=max(max(fi.c1,se.c1),se.mx-fi.mn);
        res.c2=max(max(fi.c2,se.c2),fi.mx-se.mn);
        return res;
    }
}

维护好了区间,我们自然解决了序列上的该问题,至于放在树上,我们自然可以用树链剖分来解决。

在维护树上答案时,我们的左端点时在祖先,因此我们两条链的合并还需要重新解决。

dfs(1,0);
redfs(1,1);
tree.build(1,1,n);
int Q=read();
while(Q--) {
    int u=read(),v=read(),x=read();
    int lca=LCA(u,v);
    if(lca==u) {
        node res=query(v,lca);
        cout<<res.c1<<"\n";
    } else if(lca==v) {
        node res=query(u,lca);
        cout<<res.c2<<"\n";
    } else {
        node l=query(u,lca),r=query(v,lca);
        cout<<max(max(l.c2,r.c1),r.mx-l.mn)<<"\n";
    }
    change(u,v,x);
}

总结一下,这道题我们使用了线段树的结构体合并,以及树链剖分的作用。

posted @ 2023-07-18 13:17  djh0314  阅读(19)  评论(0)    收藏  举报  来源
浏览器标题切换
浏览器标题切换end