【题目】
题目描述:
为了提高智商,ZJY 准备去往一个新世界去旅游。这个世界的城市布局像一棵树。每两座城市之间只有一条路径可以互达。每座城市都有一种宝石,有一定的价格。ZJY 为了赚取最高利益,她会选择从 A 城市买入再转手卖到 B 城市。
由于 ZJY 买宝石时经常卖萌,因而凡是 ZJY 路过的城市,这座城市的宝石价格会上涨。让我们来算算 ZJY 旅游完之后能够赚取的最大利润。(如 a a a 城市宝石价格为 v v v,则 ZJY 出售价格也为 v v v)
输入格式:
第一行输入一个正整数 n n n 表示城市个数。
接下来一行输入 n n n 个正整数表示每座城市宝石的最初价格 p p p,每个宝石的初始价格不超过 100 100 100。
第三行开始连续输入 n − 1 n-1 n−1 行,每行有两个数字 x x x 和 y y y。表示 x x x 城市和 y y y 城市有一条路径。城市编号从 1 1 1 开始。下一行输入一个正整数 Q Q Q 表示询问次数。
接下来 Q Q Q 行每行输入三个正整数 a , b , v a, b, v a,b,v,表示 ZJY 从 a a a 旅游到 b b b,城市宝石上涨 v v v。
输出格式:
对于每次询问,输出 ZJY 可能获得的最大利润,如果亏本了则输出 0 0 0。
样例数据:
输入
3
1 2 3
1 2
2 3
2
1 2 100
1 3 100
输出
1
1
提示:
【数据范围】
对于 30 % 30\% 30% 的数据,有 0 < n ≤ 100 , 0 < Q ≤ 10000 0 < n ≤ 100, 0 < Q ≤ 10000 0<n≤100,0<Q≤10000。
对于 100 % 100\% 100% 的数据,有 0 < n ≤ 50000 , 0 < Q ≤ 50000 0 < n ≤ 50000, 0 < Q ≤ 50000 0<n≤50000,0<Q≤50000。
【分析】
这道题的题目描述不太清楚啊,样例也很水,分析不出什么来,导致在考场上就差点理解错题意了
题目大意:给出一颗点带权树,从 a a a 走到 b b b(不能往回走),从这条路径上任意找两个点,求后被访问的点的权值减先被访问的点的权值的最大值,每次询问完之后给这条路径上所有点加上一个权值
很容易想到的暴力是,对于每个点,找到它前面所有点权的最小值以及后面所有点权的最大值,相减就是当前答案,扫一遍求最大值即可
可以用线段树来优化这一过程,对于线段树的一个节点,它所对应区间的答案无非就是左儿子的答案,右儿子的答案,右儿子的最大值减左儿子的最小值中取 m a x max max
而考虑到是在树上进行操作,所以用树链剖分
由于路径相当于是有向的,所以当两个端点往上跳的时候也要注意一下方向
因此,为了方便,顺便也存一下左儿子的最大值减右儿子的最小值
然后链加就很简单,直接套模板就行
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
#define M 100005
#define inf 2e+9
using namespace std;
int n,q,t,tot;
int a[N],que[N],first[N],v[M],nxt[M];
int fa[N],dep[N],son[N],top[N],pos[N],idx[N],Size[N];
struct Tree
{
int Min,Max,add,profit[2];
Tree(){Max=add=profit[0]=profit[1]=0,Min=inf;}
}Seg[N<<2];
void add(int x,int y)
{
t++;
nxt[t]=first[x];
first[x]=t;
v[t]=y;
}
void prework()
{
int x,y,i,j,end=1;
que[1]=1,dep[1]=1;
for(i=1;i<=end;++i)
{
x=que[i],Size[i]=1;
for(j=first[x];j;j=nxt[j])
{
y=v[j];
if(y==fa[x])continue;
fa[y]=x,dep[y]=dep[x]+1;
que[++end]=y;
}
}
for(i=end;i>=2;--i)
{
x=que[i],y=fa[x];
Size[y]+=Size[x];
if(Size[x]>Size[son[y]])
son[y]=x;
}
for(i=1;i<=end;++i)
{
x=que[i];
if(top[x])continue;
for(j=x;j;j=son[j])
{
pos[j]=++tot;
idx[tot]=j;
top[j]=x;
}
}
}
Tree pushup(Tree left,Tree right)
{
Tree now;
now.add=0;
now.Min=min(left.Min,right.Min);
now.Max=max(left.Max,right.Max);
now.profit[0]=max(max(left.profit[0],right.profit[0]),right.Max-left.Min);
now.profit[1]=max(max(left.profit[1],right.profit[1]),left.Max-right.Min);
return now;
}
void pushnow(int root,int k)
{
Seg[root].Max+=k;
Seg[root].Min+=k;
Seg[root].add+=k;
}
void pushdown(int root)
{
if(!Seg[root].add) return;
pushnow(root<<1,Seg[root].add);
pushnow(root<<1|1,Seg[root].add);
Seg[root].add=0;
}
void build(int root,int l,int r)
{
if(l==r)
{
Seg[root].Max=Seg[root].Min=a[idx[l]];
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
Seg[root]=pushup(Seg[root<<1],Seg[root<<1|1]);
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return x;
}
Tree query(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return Seg[root];
pushdown(root);
int mid=(l+r)>>1;
if(y<=mid) return query(root<<1,l,mid,x,y);
if(x>mid) return query(root<<1|1,mid+1,r,x,y);
return pushup(query(root<<1,l,mid,x,y),query(root<<1|1,mid+1,r,x,y));
}
int ask(int x,int y)
{
Tree ans1,ans2;
int lca=LCA(x,y);
while(top[x]!=top[lca]) ans1=pushup(query(1,1,n,pos[top[x]],pos[x]),ans1),x=fa[top[x]];
while(top[y]!=top[lca]) ans2=pushup(query(1,1,n,pos[top[y]],pos[y]),ans2),y=fa[top[y]];
if(y==lca) ans1=pushup(query(1,1,n,pos[lca],pos[x]),ans1);
else ans2=pushup(query(1,1,n,pos[lca],pos[y]),ans2);
return max(max(ans1.profit[1],ans2.profit[0]),ans2.Max-ans1.Min);
}
void modify(int root,int l,int r,int x,int y,int z)
{
if(l>=x&&r<=y)
{
pushnow(root,z);
return;
}
pushdown(root);
int mid=(l+r)>>1;
if(x<=mid) modify(root<<1,l,mid,x,y,z);
if(y>mid) modify(root<<1|1,mid+1,r,x,y,z);
Seg[root]=pushup(Seg[root<<1],Seg[root<<1|1]);
}
void Change(int x,int y,int z)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
modify(1,1,n,pos[top[x]],pos[x],z);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
modify(1,1,n,pos[x],pos[y],z);
}
int main()
{
// freopen("travel.in","r",stdin);
// freopen("travel.out","w",stdout);
int x,y,z,i;
scanf("%d",&n);
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
for(i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
prework();
build(1,1,n);
scanf("%d",&q);
for(i=1;i<=q;++i)
{
scanf("%d%d%d",&x,&y,&z);
printf("%d\n",ask(x,y));
Change(x,y,z);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}