BZOJ传送门
洛谷传送门
解析:
这类路径问题LCT和链剖都能做,不过LCT空间常数较小而时间常数较大,链剖空间常数较大且复杂度不够优秀,但是时间常数优秀。
思路:
考虑LCT上每个Splay维护这一部分链的信息怎么更新。
首先需要记录最大值和最小值,用于计算最后的收益。
考虑定向统计,从深度小的向深度大的走。
由于 m a k e r o o t makeroot makeroot操作的存在(主要是因为翻转),我们需要同时维护反向的答案。
那么我们就可以写出维护的函数 p u s h u p pushup pushup了,当然看上去有点窒息。。。
同时注意一点,由于我们维护了正反双向的答案,所以我们需要在打翻转标记的同时在这个节点上进行翻转操作,不然更新父亲节点的时候就会用到错误的答案。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const
namespace IO{
namespace IOONLY{
cs int Rlen=1<<18|1;
char buf[Rlen],*p1,*p2;
}
inline char get_char(){
using namespace IOONLY;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline ll getint(){
re ll num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline void outint(ll a){
static char ch[23];
if(a==0)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
}
using namespace IO;
cs int N=50004;
int n;
int fa[N];
ll val[N];
int son[N][2];
ll mx[N],mn[N];
ll best[N],rbest[N],add[N];
bool rev[N];
inline bool which(int now){
return son[fa[now]][1]==now;
}
inline bool isroot(int now){
return !fa[now]||(son[fa[now]][0]!=now&&son[fa[now]][1]!=now);
}
inline void pushup(int now){
mx[now]=mn[now]=val[now];
best[now]=rbest[now]=0;
re int lc=son[now][0],rc=son[now][1];
if(lc){
best[now]=max(best[now],best[lc]);
best[now]=max(best[now],val[now]-mn[lc]);
rbest[now]=max(rbest[now],rbest[lc]);
rbest[now]=max(rbest[now],mx[lc]-val[now]);
mn[now]=min(mn[now],mn[lc]);
mx[now]=max(mx[now],mx[lc]);
}
if(rc){
best[now]=max(best[now],best[rc]);
best[now]=max(best[now],mx[rc]-val[now]);
rbest[now]=max(rbest[now],rbest[rc]);
rbest[now]=max(rbest[now],val[now]-mn[rc]);
mn[now]=min(mn[now],mn[rc]);
mx[now]=max(mx[now],mx[rc]);
}
if(lc&&rc){
best[now]=max(best[now],mx[rc]-mn[lc]);
rbest[now]=max(rbest[now],mx[lc]-mn[rc]);
}
}
inline void pushadd(int now,int c){
add[now]+=c;
val[now]+=c;
mn[now]+=c;
mx[now]+=c;
}
inline void pushrev(int now){
swap(son[now][0],son[now][1]);
swap(best[now],rbest[now]);
rev[now]^=1;
}
inline void pushdown(int now){
if(rev[now]){
if(son[now][0])pushrev(son[now][0]);
if(son[now][1])pushrev(son[now][1]);
rev[now]=0;
}
if(add[now]){
if(son[now][0])pushadd(son[now][0],add[now]);
if(son[now][1])pushadd(son[now][1],add[now]);
add[now]=0;
}
}
inline void Rotate(int now){
int Fa=fa[now],FA=fa[Fa];
bool pos=which(now);
if(FA&&!isroot(Fa))son[FA][which(Fa)]=now;
son[Fa][pos]=son[now][!pos];
if(son[Fa][pos])fa[son[Fa][pos]]=Fa;
son[now][!pos]=Fa;
fa[Fa]=now;
fa[now]=FA;
pushup(Fa);
pushup(now);
}
inline void Splay(int now){
static int q[N],qn;
q[qn=1]=now;
for(int re Fa=now;!isroot(Fa);Fa=fa[Fa])q[++qn]=fa[Fa];
for(int re i=qn;i;--i)pushdown(q[i]);
for(int re Fa=fa[now];!isroot(now);Rotate(now),Fa=fa[now])
if(!isroot(Fa))Rotate(which(Fa)==which(now)?Fa:now);
}
inline void access(int now){
for(int re ch=0;now;ch=now,now=fa[now])
Splay(now),son[now][1]=ch,pushup(now);
}
inline void makeroot(int now){
access(now);
Splay(now);
pushrev(now);
}
inline ll query(int u,int v){
makeroot(u);
access(v);
Splay(v);
return best[v];
}
inline void roadadd(int u,int v,ll c){
makeroot(u);
access(v);
Splay(v);
pushadd(v,c);
}
int last[N],nxt[N<<1],to[N<<1],ecnt;
inline void addedge(int u,int v){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}
void dfs(int u){
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])
if(v^fa[u])fa[v]=u,dfs(v);
pushup(u);
}
signed main(){
n=getint();
for(int re i=1;i<=n;val[i++]=getint());
for(int re i=1;i++<n;addedge(getint(),getint()));
dfs(1);
for(int re Q=getint();Q--;pc('\n')){
int u=getint(),v=getint();ll c=getint();
outint(query(u,v));
roadadd(u,v,c);
}
return 0;
}