【2019/01/20测试T1】旅游

【题目】

传送门

题目描述:

为了提高智商,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 n1 行,每行有两个数字 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 &lt; n ≤ 100 , 0 &lt; Q ≤ 10000 0 &lt; n ≤ 100, 0 &lt; Q ≤ 10000 0<n100,0<Q10000

对于 100 % 100\% 100% 的数据,有 0 &lt; n ≤ 50000 , 0 &lt; Q ≤ 50000 0 &lt; n ≤ 50000, 0 &lt; Q ≤ 50000 0<n50000,0<Q50000


【分析】

这道题的题目描述不太清楚啊,样例也很水,分析不出什么来,导致在考场上就差点理解错题意了

题目大意:给出一颗点带权树,从 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值