P3976 [TJOI2015] 旅游 解题报告
简要题意
给一颗 \(n\) 个点的树,每个点有点权。每一次询问路径 \(u\) 到 \(v\) 上任意两个点 \(i\) 和 \(j\) ,满足先访问到 \(i\) 再访问到 \(j\) ,最大化 \(a_j-a_i\)。
数据范围:\(n,q \le 5\times 10^4\)。
分析
维护链上信息,考虑 LCT。
具体地,我们只需要维护两个子树内极值的差即可。
有一个细节:我们既需要维护左子树最大值减右子树最小值,也需要维护右子树最大值减左子树最小值,因为在区间翻转时我们会需要用到第二个信息。
代码
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;++i)
#define Down(i,s,t) for(int i=s;i>=t;--i)
#define ls (i<<1)
#define rs (i<<1|1)
#define bmod(x) ((x)>=mod?(x)-mod:(x))
#define lowbit(x) ((x)&(-(x)))
#define End {printf("NO\n");exit(0);}
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline void ckmx(int &x,int y){x=(x>y)?x:y;}
inline void ckmn(int &x,int y){x=(x<y)?x:y;}
inline void ckmx(ll &x,ll y){x=(x>y)?x:y;}
inline void ckmn(ll &x,ll y){x=(x<y)?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
inline int read(){
register int x=0,f=1;
char c=getchar();
while(c<'0' || '9'<c) f=(c=='-')?-1:1,c=getchar();
while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x*f;
}
void write(int x){
if(x>=10) write(x/10);
putchar(x%10+'0');
}
const int N=1e5+100;
int n,m,fa[N],ch[N][2],val1[N],val2[N],a[N],tag[N],tag1[N],mn[N],mx[N];
#define Get(x) (x==ch[fa[x]][1])
#define Isroot(x) (x!=ch[fa[x]][0] && x!=ch[fa[x]][1])
void push_up(int x){
mx[x]=max(max(mx[ch[x][0]],mx[ch[x][1]]),a[x]);
mn[x]=min(min(mn[ch[x][0]],mn[ch[x][1]]),a[x]);
val1[x]=max(max(val1[ch[x][0]],val1[ch[x][1]]),max(a[x],mx[ch[x][0]])-min(a[x],mn[ch[x][1]]));
val2[x]=max(max(val2[ch[x][0]],val2[ch[x][1]]),max(a[x],mx[ch[x][1]])-min(a[x],mn[ch[x][0]]));
}
void upd(int x){
if(!x) return;
swap(ch[x][0],ch[x][1]),swap(val1[x],val2[x]),tag[x]^=1;
}
void upd1(int x,int k){
if(!x) return;
mx[x]+=k,mn[x]+=k,a[x]+=k,tag1[x]+=k;
}
void push_down(int x){
if(tag[x])
upd(ch[x][0]),upd(ch[x][1]),tag[x]=0;
if(tag1[x])
upd1(ch[x][0],tag1[x]),upd1(ch[x][1],tag1[x]),tag1[x]=0;
}
void Update(int x){
if(!Isroot(x)) Update(fa[x]);
push_down(x);
}
void rotate(int x){
int y=fa[x],z=fa[y],id=Get(x);
if(!Isroot(y)) ch[z][Get(y)]=x;
ch[y][id]=ch[x][id^1],ch[x][id^1]=y;
if(ch[y][id]) fa[ch[y][id]]=y;
fa[y]=x,fa[x]=z;
push_up(y),push_up(x);
}
void Splay(int x){
Update(x);
for(int y;y=fa[x],!Isroot(x);rotate(x))
if(!Isroot(y))
rotate(Get(x)==Get(y) ? y : x);
}
int Access(int x){
int p=0;
for(p=0;x;p=x,x=fa[x])
Splay(x),ch[x][1]=p,push_up(x);
return p;
}
void makeroot(int x){
x=Access(x),upd(x);
}
void spilt(int x,int y){
makeroot(x),Access(y),Splay(y);
}
int find(int x){
Access(x),Splay(x);
push_down(x);
while(ch[x][0]) x=ch[x][0],push_down(x);
Splay(x);
return x;
}
void link(int x,int y){
if(find(x)==find(y)) return;
makeroot(x),Splay(x);
fa[x]=y;
//因为没有连实边,所以不需要维护节点信息,也不需要Splay(y)
}
void cut(int x,int y){
makeroot(x),Access(y),Splay(y);
//这个判断条件的理解:在原树上,y的父亲是x,且x没有其他实儿子。
if(ch[y][0]==x && !ch[x][1])
fa[x]=ch[y][0]=0;
}
int main()
{
#if !ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
mx[0]=-inf,mn[0]=inf;
n=read();
int u,v;
For(i,1,n) mn[i]=mx[i]=a[i]=read();
For(i,2,n) u=read(),v=read(),link(u,v);
m=read();
int delta;
For(i,1,m){
u=read(),v=read(),delta=read();
spilt(u,v);
printf("%d\n",val2[v]);
upd1(v,delta);
}
return 0;
}