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

题解 P3976 [TJOI2015] 旅游

传送门

题意

有一棵 n 个节点的树,每个节点有一个权值。
q 此操作,每次操作由 u 沿最短路径到达 v,求 valivalj 的最大值,(ij 位于此最短路径上,且先经过 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 @   djh0314  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示