【[TJOI2015]旅游-题目地址】
题目大意:
给定一棵树,每次询问从a走到b,选择这条路上的两个城市c,d,且c在d的前面(按照 a → b a\rightarrow b a→b所经过的先后顺序) p d − p c p_d-p_c pd−pc的值最大,求这个最大值,并且要求维护链上修改。
显然可以用LCT做,但是树又不是动态的,所以我们考虑用树链剖分做。
我们对于一条路径 a → b a\rightarrow b a→b,可以根据 l c a lca lca把它拆成两条链, a → l c a → b a\rightarrow lca\rightarrow b a→lca→b,然后我们考虑树剖后,维护这么几个东西:
(其实就是将其分成从深度小的走到深度大的最大和深度大的走到深度小的两种情况)
那么,我们每次在树剖查询向上跳的时候,就可以分情况讨论,当前是从下走到上的还是从上走到下的。
然后维护 a → l c a a\rightarrow lca a→lca的最小的 p p p,和 l c a → b lca\rightarrow b lca→b最大的 p p p,那么跨 l c a lca lca的最优代价就可以知道了。
代码:
#include
#include
#include
#include
using namespace std;
const int M=1e5+10;
const int inf=2e9;
int n,Q,val[M];
struct edge{
int to,last;
edge(){}
edge(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
g[++cnt]=edge(b,head[a]);head[a]=cnt;
g[++cnt]=edge(a,head[b]);head[b]=cnt;
}
int son[M],f[M],sze[M],rf[M],top[M],pos[M],dep[M],tim;
void dfs1(int a){
sze[a]=1;dep[a]=dep[f[a]]+1;
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]) continue;
f[g[i].to]=a;
dfs1(g[i].to);
sze[a]+=sze[g[i].to];
if(!son[a]||sze[son[a]]<sze[g[i].to])
son[a]=g[i].to;
}
}
void dfs2(int a,int b){
top[a]=b;rf[pos[a]=++tim]=a;
if(!son[a]) return;
dfs2(son[a],b);
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]||g[i].to==son[a]) continue;
dfs2(g[i].to,g[i].to);
}
}
int ld[M<<2],rd[M<<2],maxv[M<<2],minv[M<<2],lazy[M<<2];
void pushup(int o){
maxv[o]=max(maxv[o<<1],maxv[o<<1|1]);
minv[o]=min(minv[o<<1],minv[o<<1|1]);
ld[o]=max(max(ld[o<<1],ld[o<<1|1]),maxv[o<<1|1]-minv[o<<1]);
rd[o]=max(max(rd[o<<1],rd[o<<1|1]),maxv[o<<1]-minv[o<<1|1]);
}
void addv(int o,int v){
maxv[o]+=v;minv[o]+=v;lazy[o]+=v;
}
void pushdown(int o){
if(lazy[o]){
addv(o<<1,lazy[o]);
addv(o<<1|1,lazy[o]);
lazy[o]=0;
}
}
void build(int o,int l,int r){
if(l==r){
maxv[o]=minv[o]=val[rf[l]];
ld[o]=rd[o]=-inf;
return;
} int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
void update(int o,int l,int r,int L,int R,int v){
if(L<=l&&r<=R){
addv(o,v);return;
} int mid=l+r>>1;
pushdown(o);
if(L<=mid) update(o<<1,l,mid,L,R,v);
if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
pushup(o);
}
int Maxv,Minv,Bmax;
int maxt,mint;
void query(int o,int l,int r,int L,int R,int tp){
if(L<=l&&r<=R){
Maxv=max(Maxv,maxv[o]);
Minv=min(Minv,minv[o]);
if(tp==0){
Bmax=max(Bmax,ld[o]);
}else{
Bmax=max(Bmax,rd[o]);
}
maxt=maxv[o];mint=minv[o];
return;
} int mid=l+r>>1;
pushdown(o);
if(R<=mid) query(o<<1,l,mid,L,R,tp);
else if(L>mid) query(o<<1|1,mid+1,r,L,R,tp);
else{
query(o<<1,l,mid,L,R,tp);
int max1=maxt,min1=mint;
query(o<<1|1,mid+1,r,L,R,tp);
int max2=maxt,min2=mint;
if(!tp) Bmax=max(Bmax,max2-min1);
else Bmax=max(Bmax,max1-min2);
maxt=max(max1,max2);mint=min(min1,min2);
}
}
int ask(int a,int b){
int ans=0;
int rmaxv=-inf,lminv=inf;
while(top[a]!=top[b]){
Maxv=-inf;Minv=inf;Bmax=-inf;
if(dep[top[a]]<dep[top[b]]){
query(1,1,n,pos[top[b]],pos[b],0);
ans=max(ans,max(Bmax,rmaxv-Minv));
rmaxv=max(rmaxv,Maxv);
b=f[top[b]];
}else{
query(1,1,n,pos[top[a]],pos[a],1);
ans=max(ans,max(Bmax,Maxv-lminv));
lminv=min(lminv,Minv);
a=f[top[a]];
}
} if(dep[a]<dep[b]){
Maxv=-inf;Minv=inf;Bmax=-inf;
query(1,1,n,pos[a],pos[b],0);
ans=max(ans,max(Bmax,Maxv-lminv));
lminv=min(lminv,Minv);
}else{
Maxv=-inf;Minv=inf;Bmax=-inf;
query(1,1,n,pos[b],pos[a],1);
ans=max(ans,max(Bmax,rmaxv-Minv));
rmaxv=max(rmaxv,Maxv);
} return max(ans,rmaxv-lminv);
}
void addval(int a,int b,int v){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
update(1,1,n,pos[top[a]],pos[a],v);
a=f[top[a]];
} if(dep[a]>dep[b])swap(a,b);
update(1,1,n,pos[a],pos[b],v);
}
int a,b,v;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b);
}
dfs1(1);
dfs2(1,1);
build(1,1,n);
scanf("%d",&Q);
while(Q--){
scanf("%d%d%d",&a,&b,&v);
printf("%d\n",ask(a,b));
addval(a,b,v);
} return 0;
}