[TJOI2015]旅游

问题描述

为了提高智商,ZJY准备去往一个新世界去旅游。这个世界的城市布局像一棵树。每两座城市之间只有一条路径可以互达。每座城市都有一种宝石,有一定的价格。ZJY为了赚取最高利益,她会选择从A城市买入再转手卖到B城市。由于ZJY买宝石时经常卖萌,因而凡是ZJY路过的城市,这座城市的宝石价格会上涨。让我们来算算ZJY旅游完之后能够赚取的最大利润。(如a城市宝石价格为v,则ZJY出售价格也为v)

输入格式

第一行输入一个正整数N,表示城市个数。
接下来一行输入N个正整数表示每座城市宝石的最初价格p。
第三行开始连续输入N-1行,每行有两个数字x和y。表示x城市和y城市有一条路径。城市编号从1开始。
下一行输入一个整数Q,表示询问次数。
接下来Q行,每行输入三个正整数a,b,v,表示ZJY从a旅游到b,城市宝石上涨v。
即是询问a—>b(有方向)间路径上的max(Price[j]−Price[i])) 且j到a的距离比i到a大 。然后把这条路径上所有点的点权+v。

输出格式

对于每次询问,输出ZJY可能获得的最大利润,如果亏本则输出0。

样例输入 1

3
1 2 3
1 2
2 3
2
1 2 100
1 3 100

样例输出 1

1
1

样例输入 2

3
1 2 3
1 3
2 3
2
1 2 100
1 3 100

样例输出 2

2
2

数据范围

1≤ N≤50000, 1≤Q ≤50000,每个宝石的初始价格不超过100


题解

啊啊调了一上午。
原问题实际上是两个操作:修改和求值。
用树剖。修改简单,不讲了。
恶心的是求值,不能简单地求极大值与极小值之差。所以我们要多维护几个东西。LeftDelta[]维护每个区间的极大极小值之差,要求极小值在左。RightDelta[]维护极小值在右的情况。
对于一条路径, “买”和“卖”的点可能在一条重链上,也可能不在。
若在,区间查询时可以处理出来;若不在,记下之前的最小值,对于每条链,用该链的区间最大值减去之前的极小值即是在该链“卖”的最大收益。对于每种情况,取最大值。
具体实现略有不同,见代码。


代码

#include <stdio.h>
#include <cstdio>
#include <algorithm>
#include <iostream>
#define ll long long
using namespace std;
struct dt{
    ll maxit,minit,ldelta,rdelta;
};
const ll Q=50005;
ll ls[Q<<3],rs[Q<<3],ldel[Q<<3],rdel[Q<<3],lazy[Q<<3],maxn[Q<<3],minn[Q<<3],tot=1;
ll cnt=0,dep[Q],f[Q],si[Q],bs[Q],id[Q],be[Q];
ll n,nn[Q<<1],e[Q<<1],last[Q],inc=0,a[Q];
void add(ll x,ll y)
{
    e[++inc]=y;
    nn[inc]=last[x];
    last[x]=inc;
}
dt get(ll now,ll l,ll r,ll x,ll y,ll v)
{
    dt t,t1,t2;
    if(x<=l&&y>=r){
        t.ldelta=ldel[now],t.rdelta=rdel[now];
        t.maxit=maxn[now],t.minit=minn[now];
        lazy[now]+=v;
        maxn[now]+=v;
        minn[now]+=v;
        return t;
    }
    t.ldelta=t.rdelta=0;
    t.minit=1e9;
    t.maxit=-1e9;
    ll mid=(l+r)>>1;
    if(lazy[now])
    {
        ll ha=lazy[now];
        lazy[now]=0;
        if(!ls[now])ls[now]=++tot;
        if(!rs[now])rs[now]=++tot;
        lazy[ls[now]]+=ha;
        lazy[rs[now]]+=ha;
        maxn[ls[now]]+=ha;
        maxn[rs[now]]+=ha;
        minn[ls[now]]+=ha;
        minn[rs[now]]+=ha;
    }
    if(x<=mid){
        if(!ls[now])ls[now]=++tot;
        t1=get(ls[now],l,mid,x,y,v);
        t.maxit=max(t.maxit,t1.maxit);
        t.minit=min(t.minit,t1.minit);
        t.ldelta=max(t.ldelta,t1.ldelta);
        t.rdelta=max(t.rdelta,t1.rdelta);
    }
    if(y>mid){
        if(!rs[now])rs[now]=++tot;
        t2=get(rs[now],mid+1,r,x,y,v);
        t.maxit=max(t.maxit,t2.maxit);
        t.minit=min(t.minit,t2.minit);
        t.ldelta=max(t.ldelta,t2.ldelta);
        t.rdelta=max(t.rdelta,t2.rdelta);
    }
    if(x<=mid&&y>mid){
        t.ldelta=max(t.ldelta,t2.maxit-t1.minit);
        t.rdelta=max(t.rdelta,t1.maxit-t2.minit);
    }
    minn[now]=min(minn[ls[now]],minn[rs[now]]);
    maxn[now]=max(maxn[ls[now]],maxn[rs[now]]);
    ldel[now]=max(max(ldel[ls[now]],ldel[rs[now]]),maxn[rs[now]]-minn[ls[now]]);
    rdel[now]=max(max(rdel[ls[now]],rdel[rs[now]]),maxn[ls[now]]-minn[rs[now]]);
    return t;
}
void fbc(ll x)
{
    si[x]=1,bs[x]=0;
    ll now=0,y,t;
    for(t=last[x];t;t=nn[t])
    {
        y=e[t];
        if(y==f[x])continue;
        dep[y]=dep[x]+1,f[y]=x;
        fbc(y);
        si[x]+=si[y];
        if(now<si[y])
            now=si[y],bs[x]=y;
    }
}
void cbc(ll x,ll now)
{
    id[x]=++cnt,be[x]=now;
    if(bs[x]!=0)cbc(bs[x],now);
    ll y,t;
    for(t=last[x];t;t=nn[t])
    {
        y=e[t];
        if(y==f[x]||y==bs[x])continue;
        cbc(y,y);
    }
}
int main()
{
    minn[0]=1e9;
    ll i,m,x,y;
    scanf("%lld",&n);
    for(i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(i=1;i<n;i++)
        scanf("%lld%lld",&x,&y),add(x,y),add(y,x);
    dep[1]=1,f[1]=0;
    fbc(1);
    cbc(1,1);
    for(i=1;i<=n;i++)
        get(1,1,n,id[i],id[i],a[i]);
    for(scanf("%lld",&i);i;--i)
    {
        scanf("%lld%lld%lld",&x,&y,&m);
        ll ans=0,lmin=1e9,lmax=-1e9,rmin=1e9,rmax=-1e9;
        dt ha;
        while(be[x]!=be[y])
        {
            if(dep[be[x]]>dep[be[y]])
            {
                ha=get(1,1,n,id[be[x]],id[x],m);
                ans=max(ans,ha.rdelta);
                lmax=ha.maxit;
                ans=max(ans,lmax-lmin);
                lmin=min(lmin,ha.minit);
                ans=max(ans,rmax-lmin);
                x=f[be[x]];
            }
            else{
                ha=get(1,1,n,id[be[y]],id[y],m);
                ans=max(ans,ha.ldelta);
                rmin=ha.minit;
                ans=max(ans,rmax-rmin);
                rmax=max(rmax,ha.maxit);
                ans=max(ans,rmax-lmin);
                y=f[be[y]];
                }
        }
        ha=get(1,1,n,min(id[x],id[y]),max(id[x],id[y]),m);
        if(id[x]<id[y])ans=max(ans,ha.ldelta);
        else ans=max(ans,ha.rdelta);
        ans=max(ans,max(rmax-ha.minit,ha.maxit-lmin));
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值