问题描述
为了提高智商,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;
}