Yaoge’s maximum profit(树链剖分 维护两个方向的最值)

本文介绍了一种使用树链剖分解决特定路径区间查询问题的方法,包括维护区间最大值、最小值以及上下方向的最大收益,通过实例代码详细解释了算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

original link - http://acm.hdu.edu.cn/showproblem.php?pid=5052

题意:

给出一棵树,点权。每次查询要从xxx走到yyy,可以在路径之前的地方取一个值v1v_1v1,之后的地方v2v_2v2,你可以得到v2−v1v_2-v_1v2v1,当然可以不取为0。求每次得到的最大值。走完后,路径上的点权值加上vvv

解析:

考虑用树链剖分做。

分为三种情况:

  • x→lcax\to lcaxlca
  • lca→ylca\to ylcay
  • x→yx\to yxy

我们要维护区间最大值mxmxmx,最小值mimimi,往上时可得到的最大值upupup,往下时可得到的最大值downdowndown

由于x→lcax\to lcaxlca的数组是lca→xlca\to xlcax,所以downdowndown表示正常的mx[rs]−mi[ls]mx[rs]-mi[ls]mx[rs]mi[ls],而upupup则相反为mx[ls]−mi[rs]mx[ls]-mi[rs]mx[ls]mi[rs]

最后的答案为:max(up[x→lca],down[lca→y],mx[lca→y]−mi[x→lca]max(up[x\to lca],down[lca\to y],mx[lca\to y]-mi[x\to lca]max(up[xlca],down[lcay],mx[lcay]mi[xlca]


想的时候,比较纠结的是路径上多个区间之间的关系。对后面来说,mimimimxmxmx的维护比较简单,但是upupupdowndowndown要想一下。

考虑x→lcax\to lcaxlca时,之前的结点为UUU,当前区间QQQ,此时得到的upupup应该是Q.mx−U.miQ.mx-U.miQ.mxU.mi,合并的时候注意匹配即可。


代码:

/*
 *  Author : Jk_Chen
 *    Date : 2019-08-20-13.24.20
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
const LL mod=1e9+7;
const int maxn=5e4+5;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/

#define rep_e(i,p,u) for(int i=head[p],u=to[i];i;i=nex[i],u=to[i])
int head[maxn],to[maxn<<1],nex[maxn<<1],now;
void add(int a,int b){
    nex[++now]=head[a];head[a]=now;to[now]=b;
}
void init_edge(){
    memset(head,0,sizeof head);
    now=0;
}
/*_________________________________________________________edge*/

int n;
int a[maxn],w[maxn];

// 跑出树的信息
int fa[maxn],dep[maxn],siz[maxn],son[maxn];
void dfs_init(int p,int f,int deep){
    fa[p]=f;
    dep[p]=deep;
    siz[p]=1;
    son[p]=0;
    int maxx=-1;
    rep_e(i,p,u){
        if(u==f)continue;
        dfs_init(u,p,deep+1);
        siz[p]+=siz[u];
        if(siz[u]>maxx){
            maxx=siz[u];
            son[p]=u;
        }
    }
}

// 跑出对应dfs序
int top[maxn],id[maxn];
int cnt;
void dfs(int p,int rt){
    top[p]=rt;
    id[p]=++cnt;
    w[cnt]=a[p];
    if(!son[p])return;
    // 先跑重儿子
    dfs(son[p],rt);
    rep_e(i,p,u){
        if(u==fa[p]||u==son[p])continue;
        dfs(u,u); // 不是重儿子开新链
    }
}
/*_________________________________________________________init*/

#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
struct node{
    int mx,mi,up,down;
    node(int mx=0,int mi=2e9,int up=0,int down=0):mx(mx),mi(mi),up(up),down(down){}
}tr[maxn<<2];
int laz[maxn<<2];
void Merge(node &res,node l,node r){
    res.mx=max(l.mx,r.mx);
    res.mi=min(l.mi,r.mi);
    res.down=max(max(l.down,r.down), r.mx-l.mi);
    res.up=max(max(l.up,r.up), l.mx-r.mi);
}
void build(int rt=1,int l=1,int r=n){
    if(l==r){
        tr[rt]=node(w[l],w[l],0,0);
        laz[rt]=0;
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    Merge(tr[rt],tr[ls],tr[rs]);
    laz[rt]=0;
}
void down(int rt){
    if(laz[rt]){
        int &val=laz[rt];
        tr[ls].mx+=val;
        tr[ls].mi+=val;
        tr[rs].mx+=val;
        tr[rs].mi+=val;
        laz[ls]+=val;
        laz[rs]+=val;
        laz[rt]=0;
    }
}
void update(int L,int R,int val,int rt=1,int l=1,int r=n){
    if(l>=L&&r<=R){
        tr[rt].mi+=val;
        tr[rt].mx+=val;
        laz[rt]+=val;
        return;
    }
    down(rt);
    if(L<=mid)update(L,R,val,ls,l,mid);
    if(R>mid)update(L,R,val,rs,mid+1,r);
    Merge(tr[rt],tr[ls],tr[rs]);
}
node query(int L,int R,int rt=1,int l=1,int r=n){
    if(l>=L&&r<=R){
        return tr[rt];
    }
    down(rt);
    node res;
    if(L<=mid)res=query(L,R,ls,l,mid);
    if(R>mid)Merge(res,res,query(L,R,rs,mid+1,r));
    return res;
}
/*_________________________________________________________tree*/

int solve(int x,int y,int val){
    node U,D;
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]]){
            Merge(U,query(id[top[x]],id[x]),U); // up l-r
            update(id[top[x]],id[x],val);
            x=fa[top[x]];
        }
        else{
            Merge(D,query(id[top[y]],id[y]),D); // down r-l
            update(id[top[y]],id[y],val);
            y=fa[top[y]];
        }
    }
    if(dep[x]<=dep[y]){
        Merge(D,query(id[x],id[y]),D);
        update(id[x],id[y],val);
    }
    else{
        Merge(U,query(id[y],id[x]),U);
        update(id[y],id[x],val);
    }
    return max(max(U.up,D.down), max(0,D.mx-U.mi));
}
/*_________________________________________________________solve*/

int main(){int t=rd();while(t--){
    init_edge();
    cnt=0;
    n=rd();
    rep(i,1,n)a[i]=rd();
    rep(i,2,n){
        int u=rd(),v=rd();
        add(u,v);add(v,u);
    }
    int rt=1;
    dfs_init(rt,0,1);    // rt 0 1
    dfs(rt,rt);          // rt rt
    build();
    int m=rd();
    while(m--){
        int a=rd(),b=rd(),v=rd();
        printf("%d\n",solve(a,b,v));
    }
}}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值