BZOJ3999
来自蒟蒻XXJ的做题记录
其实这个题目就是一个比较裸的树剖w然后再加上一个线段树维护
首先看题目我们要解决的是一个求解区间里两个数之间差的最大值【绕
而且我们发现这两个数的关系还必须在路径上是有向的,也就是说必须是一个后走到的点减去一个先走到的点【雾
【树剖应该不用我说了吧 =w=
现在我们用线段树维护这样的四个量:
从编号小的向编号大的走可以得到的最大利润 lans
从编号大的向编号小的走可以得到的最大利润 rans
区间最大值 imax
区间最小值 imin
在合并线段树节点两边信息的时候我们用这样的一个式子:
\(lans[i]=max(max(lans[lc],lans[rc]),imax[lc]-imin[rc])\)
\(rans[i]=max(max(rans[lc],rans[rc]),imax[rc]-imin[lc])\)
\(imax[i]=max(imax[lc],imax[rc])\)
\(imin[i]=min(imin[lc],imin[rc])\)
然后更新就可以辣
现在就是树剖的时候 该怎么爬树
一般的树剖w是直接把深度大的交换之类的w但是现在涉及到一个方向问题w 所以我直接就比较了两个top
的深度然后直接暴力爬ww【雾】写的太丑了
然后由起点向上爬的时候就记录一个之前出现的最小值 lmin
然后用每次查出来的区间最大值去更新下 ans
这就表示在起点到LCA路径上的收益最大值
对于终点向上爬的时候同理维护一个之前出现的最大值 rmax
然后xjb跟之前一样搞一下 更新 ans
最后的时候再用 (rmax−lmin)
更新一下 ans
就可以了w
看代码吧w:
#include<bits/stdc++.h>
#define GO(i,here) for(int i=head[here];i!=-1;i=nex[i])
#define mem(i,j) memset(i,j,sizeof(i))
#define lc (i<<1)+1
#define rc (i<<1)+2
using namespace std;
const int MAXN=500001;
const int INF=1008610086;
int in(){
int a(0),f(1);char c=getchar();
while(c<'0'||c>'9') if(c=='-') f=-1,c=getchar();
while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+c-'0',c=getchar();
return a*f;
}
int n,num[MAXN];
int dep[MAXN],fa[MAXN],siz[MAXN],son[MAXN],top[MAXN],rank[MAXN],r[MAXN],tot;
int head[MAXN],to[2*MAXN],nex[2*MAXN],top1;
struct Ans{
int imax,imin,lans,rans;
};
struct Tree{
int imax[2*MAXN+10],imin[2*MAXN+10];
int lans[2*MAXN+10],rans[2*MAXN+10];//lans表示的是 左边大右边小 rans表示的是右边大左边小
int cha[2*MAXN+10];
void build_tree(int l,int r,int i){
if(l==r){
lans[i]=rans[i]=0;imax[i]=imin[i]=num[rank[l]];
return;
}
int mid=(l+r)>>1;
build_tree(l,mid,lc);
build_tree(mid+1,r,rc);
lans[i]=max(max(lans[lc],lans[rc]),imax[lc]-imin[rc]);rans[i]=max(max(rans[lc],rans[rc]),imax[rc]-imin[lc]);
imax[i]=max(imax[lc],imax[rc]);imin[i]=min(imin[lc],imin[rc]);
}
void pushdown(int i){
if(!cha[i]) return;
imax[lc]+=cha[i];imin[lc]+=cha[i];
imax[rc]+=cha[i];imin[rc]+=cha[i];
cha[lc]+=cha[i];cha[rc]+=cha[i];
cha[i]=0;
}
void pushup(int i,int k){
cha[i]+=k;
imax[i]+=k;imin[i]+=k;
}
void update(int fl,int fr,int l,int r,int k,int i){
if(fl<=l&&r<=fr){pushup(i,k);return;}
if(fl>r||fr<l) return;
pushdown(i);
int mid=(l+r)>>1;
update(fl,fr,l,mid,k,lc);update(fl,fr,mid+1,r,k,rc);
lans[i]=max(max(lans[lc],lans[rc]),imax[lc]-imin[rc]);rans[i]=max(max(rans[lc],rans[rc]),imax[rc]-imin[lc]);
imax[i]=max(imax[lc],imax[rc]);imin[i]=min(imin[lc],imin[rc]);
}
Ans query(int fl,int fr,int l,int r,int i){
if(fl<=l&&r<=fr){
Ans tmp;tmp.imax=imax[i];tmp.imin=imin[i];tmp.lans=lans[i];tmp.rans=rans[i];
return tmp;
}
if(fl>r||fr<l){
Ans tmp;tmp.imin=INF;tmp.imax=0;tmp.lans=0;tmp.rans=0;
return tmp;
}
pushdown(i);
int mid=(l+r)>>1;
Ans ans,
t1=query(fl,fr,l,mid,lc),
t2=query(fl,fr,mid+1,r,rc);
ans.imax=max(t1.imax,t2.imax);
ans.imin=min(t1.imin,t2.imin);
ans.lans=max(max(t1.lans,t2.lans),t1.imax-t2.imin);
ans.rans=max(max(t1.rans,t2.rans),t2.imax-t1.imin);
lans[i]=max(max(lans[lc],lans[rc]),imax[lc]-imin[rc]);rans[i]=max(max(rans[lc],rans[rc]),imax[rc]-imin[lc]);
imax[i]=max(imax[lc],imax[rc]);imin[i]=min(imin[lc],imin[rc]);
return ans;
}
}tree;
void add(int a,int b){
nex[top1]=head[a];head[a]=top1;to[top1++]=b;
nex[top1]=head[b];head[b]=top1;to[top1++]=a;
}
void dfs1(int here,int d,int f){
siz[here]=1;son[here]=-1;dep[here]=d;fa[here]=f;
int qwq(0);
GO(i,here){
if(to[i]==f) continue;
dfs1(to[i],d+1,here);
siz[here]+=siz[to[i]];
if(qwq<siz[to[i]]) qwq=siz[son[here]=to[i]];
}
}
void dfs2(int here,int tp){
rank[tot++]=here;top[here]=tp;r[here]=tot-1;
if(son[here]==-1) return;
dfs2(son[here],tp);
GO(i,here){
if(to[i]==son[here]||fa[here]==to[i]) continue;
dfs2(to[i],to[i]);
}
}
void input(){
mem(head,-1);
n=in();
for(int i=1;i<=n;i++) num[i]=in();
for(int i=1;i<n;i++){
int a,b;
a=in();b=in();
add(a,b);
}
dfs1(1,0,0);
dfs2(1,1);
tree.build_tree(0,tot-1,0);
}
void xxj(){
int m;
m=in();
for(int i=1;i<=m;++i){
int a,b,v;
a=in();b=in();
v=in();int lmin(INF),rmax(0),ans(0);
while(top[a]!=top[b]){
if(dep[top[a]]>dep[top[b]]){//这个时候将a向上移动w 这个时候因为编号是递减的 所以说只需要看rans就可以了w然后用
Ans tmp=tree.query(r[top[a]],r[a],0,tot-1,0);
tree.update(r[top[a]],r[a],0,tot-1,v,0);a=fa[top[a]];
ans=max(ans,tmp.lans);ans=max(ans,tmp.imax-lmin);
lmin=min(lmin,tmp.imin);
}
else{//这个时候b向上移动w 这个时候因为编号是递减的 所以说要lans
Ans tmp=tree.query(r[top[b]],r[b],0,tot-1,0);
tree.update(r[top[b]],r[b],0,tot-1,v,0);b=fa[top[b]];
ans=max(ans,tmp.rans);ans=max(ans,rmax-tmp.imin);
rmax=max(rmax,tmp.imax);
}
}
if(dep[a]>dep[b]){//a向上爬
Ans tmp=tree.query(r[b],r[a],0,tot-1,0);
tree.update(r[b],r[a],0,tot-1,v,0);
ans=max(ans,tmp.lans);ans=max(ans,tmp.imax-lmin);
lmin=min(lmin,tmp.imin);
}
else{//b向上爬
Ans tmp=tree.query(r[a],r[b],0,tot-1,0);
tree.update(r[a],r[b],0,tot-1,v,0);
ans=max(ans,tmp.rans);ans=max(ans,rmax-tmp.imin);
rmax=max(rmax,tmp.imax);
}
ans=max(ans,rmax-lmin);
printf("%d\n",ans);
}
}
int main(){
input();
xxj();
return 0;
}