UOJ Logo Xiaojian_xiang的博客

博客

BZOJ3999

2017-02-18 19:56:41 By Xiaojian_xiang

来自蒟蒻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;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。