Yaoge’s maximum profit
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1321 Accepted Submission(s): 418
Problem Description
Yaoge likes to eat chicken chops late at night. Yaoge has eaten too many chicken chops, so that Yaoge knows the pattern in the world of chicken chops. There are N cities in the world numbered from 1 to N . There are some roads between some cities, and there is one and only one simple path between each pair of cities, i.e. the cities are connected like a tree. When Yaoge moves along a path, Yaoge can choose one city to buy ONE chicken chop and sell it in a city after the city Yaoge buy it. So Yaoge can get profit if Yaoge sell the chicken chop with higher price. Yaoge is famous in the world. AFTER Yaoge has completed one travel, the price of the chicken chop in each city on that travel path will be increased by V .
Input
The first line contains an integer T (0 < T ≤ 10), the number of test cases you need to solve. For each test case, the first line contains an integer N (0 < N ≤ 50000), the number of cities. For each of the next N lines, the i-th line contains an integer Wi(0 < Wi ≤ 10000), the price of the chicken chop in city i. Each of the next N - 1 lines contains two integers X Y (1 ≤ X, Y ≤ N ), describing a road between city X and city Y . The next line contains an integer Q(0 ≤ Q ≤ 50000), the number of queries. Each of the next Q lines contains three integer X Y V(1 ≤ X, Y ≤ N ; 0 < V ≤ 10000), meaning that Yaoge moves along the path from city X to city Y , and the price of the chicken chop in each city on the path will be increased by V AFTER Yaoge has completed this travel.
Output
For each query, output the maximum profit Yaoge can get. If no positive profit can be earned, output 0 instead.
Sample Input
15123451 22 33 44 551 5 15 1 11 1 25 1 11 2 1
Sample Output
40010
题意:yaoge想要通过买卖鸡排赚差价,有n个城市,每个城市有一个鸡排价格,然后有n-1条边(将城市像树一样连起来),下面有q次操作,每次从城市u到城市v(走最短路),在这条路上的城市鸡排价格都增加x,求每次yaoge的最大收益
分析:
题目中已经说出了,给的是一棵树,操作数很多,而且每次对(u,v)的最短路进行操作,可以用(树剖)解决
现在我们开始考虑线段树应该维护哪些值?
由于每次从城市u到城市v,要完成买和卖的这个操作肯定是先买才能卖
所以我们需要维护的是一个先买再卖所得收益的最大值,但结合树剖来看,树剖中每个点都向他的祖先节点找,(x-->fx),(y-->fy),
比如这张图
现在要更新x-->y的路径,那么在树剖中,会更新 (fx-->x),(y-->fy)这样的链,由于我们想要的顺序是(x-->fx)(y-->fy)这种,所以在维护(fx-->x)时就要维护一个相反值,即先卖后买的最大值,这样 fx走到x先卖后买,那么反向考虑的话,x走到fx先买后卖(其实也是用了向量的思想)
于是维护两个值就行了,对于 (fx-->x)这样的链维护先卖后买的最大收益(rtl)
(y-->fy)这样正常方向的链,维护先买后卖的最大收益(ltr),
为了维护这两个值,还需要维护区间最大值(maxx)和区间最小值(minn)
这样 ltr=右儿子的maxx-左儿子的minn rtl=左儿子的maxx-右儿子的minn
查询的时候还需要区间合并(这点详细看代码把)
树剖时,1.查询x-->fx这种的链,则查询从左到右的最大收益 2.查询y-->fy这种的链,则查询从右到左的最大收益
然后维护一个当前链的左边最小值(不在链内)minu,当前链右边最大值(不在链内)maxv,然后查询出(在链内)当前这条链的最小值tmin和最大值tmax
所以刷新最大收益为max( maxv-tmin , tmax-minu , 这条链的链内最大收益)
以上
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
const int N=5e4+10;
typedef long long ll;
ll siz[N],faz[N],tid[N],rnk[N],son[N],top[N],dep[N];
ll cnt,tot,a[N],first[N];
struct node
{
ll l,r,lazy,maxx,minn,ltr,rtl;
ll mid()
{
return (l+r)>>1;
}
} t[N<<2];
struct edge
{
ll v,next;
}g[N<<2];
void add(ll u,ll v)
{
g[tot].v=v;
g[tot].next=first[u];
first[u]=tot++;
}
void dfs1(ll u,ll father,ll depth)
{
siz[u]=1;
faz[u]=father;
dep[u]=depth;
for(ll i=first[u]; ~i; i=g[i].next)
{
ll v=g[i].v;
if(v!=father)
{
dfs1(v,u,depth+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]])
son[u]=v;
}
}
}
void dfs2(ll u,ll t)
{
tid[u]=++cnt;
rnk[cnt]=u;
top[u]=t;
if(son[u]==-1)return;
dfs2(son[u],t);
for(ll i=first[u]; ~i; i=g[i].next)
{
ll v=g[i].v;
if(v!=faz[u]&&v!=son[u])
dfs2(v,v);
}
}
void pushup(ll rt)
{
t[rt].maxx=max(t[lson].maxx,t[rson].maxx);
t[rt].minn=min(t[lson].minn,t[rson].minn);
t[rt].ltr=max(t[rson].maxx-t[lson].minn,max(t[lson].ltr,t[rson].ltr));
t[rt].rtl=max(t[lson].maxx-t[rson].minn,max(t[lson].rtl,t[rson].rtl));
}
void pushdown(ll rt)
{
if(t[rt].lazy)
{
t[lson].lazy+=t[rt].lazy;
t[rson].lazy+=t[rt].lazy;
t[lson].maxx+=t[rt].lazy;
t[rson].maxx+=t[rt].lazy;
t[lson].minn+=t[rt].lazy;
t[rson].minn+=t[rt].lazy;
t[rt].lazy=0;
}
}
void build(ll l,ll r,ll rt)
{
t[rt].l=l,t[rt].r=r,t[rt].lazy=0;
if(l==r)
{
t[rt].maxx=t[rt].minn=a[rnk[l]];
t[rt].ltr=t[rt].rtl=0;
return;
}
ll m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);
}
void update(ll ql,ll qr,ll rt,ll w)
{
if(ql<=t[rt].l&&t[rt].r<=qr)
{
t[rt].lazy+=w;
t[rt].maxx+=w;
t[rt].minn+=w;
return;
}
pushdown(rt);
ll m=t[rt].mid();
if(ql<=m)update(ql,qr,lson,w);
if(qr>m)update(qr,qr,rson,w);
pushup(rt);
}
ll query(ll ql,ll qr,ll rt,ll flag,ll &tmax,ll &tmin)
{
if(ql<=t[rt].l&&t[rt].r<=qr)
{
tmax=t[rt].maxx;
tmin=t[rt].minn;
if(flag)return t[rt].ltr;//从左到右
else return t[rt].rtl;//从右到左
}
pushdown(rt);
ll m=t[rt].mid();
if(qr<=m)return query(ql,qr,lson,flag,tmax,tmin);
else if(ql>m)return query(ql,qr,rson,flag,tmax,tmin);
else//区间合并
{
ll ans=0,maxl,minl,maxr,minr;
ans=max(query(ql,m,lson,flag,maxl,minl),query(m+1,qr,rson,flag,maxr,minr));
if(flag)ans=max(ans,maxr-minl);
else ans=max(ans,maxl-minr);
tmax=max(maxl,maxr);
tmin=min(minl,minr);
return ans;
}
}
ll solve(ll u,ll v,ll w)
{
ll maxv=0,minu=t[1].maxx,ans=0,tmax,tmin;
while(top[u]!=top[v])
{
if(dep[top[u]]>=dep[top[v]])
{
ans=max(ans,query(tid[top[u]],tid[u],1,0,tmax,tmin));//u则查询从左到右
ans=max(ans,maxv-tmin);
ans=max(ans,tmax-minu);
minu=min(minu,tmin);
update(tid[top[u]],tid[u],1,w);
u=faz[top[u]];
}
else
{
ans=max(ans,query(tid[top[v]],tid[v],1,1,tmax,tmin));//v则从右到左
ans=max(ans,maxv-tmin);
ans=max(ans,tmax-minu);
maxv=max(maxv,tmax);
update(tid[top[v]],tid[v],1,w);
v=faz[top[v]];
}
}
if(dep[u]>=dep[v])
{
ans=max(ans,query(tid[v],tid[u],1,0,tmax,tmin));
ans=max(ans,maxv-tmin);
ans=max(ans,tmax-minu);
update(tid[v],tid[u],1,w);
}
else
{
ans=max(ans,query(tid[u],tid[v],1,1,tmax,tmin));
ans=max(ans,tmax-minu);
ans=max(ans,maxv-tmin);
update(tid[u],tid[v],1,w);
}
return ans;
}
void init(ll n)
{
memset(son,-1,sizeof(son));
memset(first,-1,sizeof(first));
cnt=tot=0;
}
int main()
{
ll t,u,v,w,n,q;
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
init(n);
for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
for(ll i=2;i<=n;i++)
{
scanf("%lld%lld",&u,&v);
add(u,v);
add(v,u);
}
dfs1(1,1,1);
dfs2(1,1);
build(1,n,1);
scanf("%lld",&q);
while(q--)
{
scanf("%lld%lld%lld",&u,&v,&w);
printf("%lld\n",solve(u,v,w));
}
}
return 0;
}