POJ3728(The merchant)

The merchant

Description

There are N cities in a country, and there is one and only one simple path between each pair of cities. A merchant has chosen some paths and wants to earn as much money as possible in each path. When he move along a path, he can choose one city to buy some goods and sell them in a city after it. The goods in all cities are the same but the prices are different. Now your task is to calculate the maximum possible profit on each path.

Input

The first line contains N, the number of cities.
Each of the next N lines contains wi the goods’ price in each city.
Each of the next N-1 lines contains labels of two cities, describing a road between the two cities.
The next line contains Q, the number of paths.
Each of the next Q lines contains labels of two cities, describing a path. The cities are numbered from 1 to N.

1 ≤ N, wi, Q ≤ 50000

Output

The output contains Q lines, each contains the maximum profit of the corresponding path. If no positive profit can be earned, output 0 instead.

Sample Input

4
1
5
3
2
1 3
3 2
3 4
9
1 2
1 3
1 4
2 3
2 1
2 4
3 1
3 2
3 4
Sample Output

4
2
2
0
0
0
0
2
0

题意

确实有N一个国家的城市,每一对城市之间只有一条简单的道路。一位商人选择了一些道路并希望在每条道路上尽可能多地赚到钱。当他沿着一条路走时他可以选择一座城市购买一些商品然后再在城市里出售,(不能走回头路),问最多正赚多少差价。

思路

在线倍增LCA + dp,维护五个参数:

  1. f[x][i]表示i节点上跳2^j次的祖先节点
  2. maxv[i][j] 表示从i 节点 到2^j的节点最大权值
  3. minv[i][j] 表示从i 节点 到2^j的节点最小权值
  4. up[i][j]表示i节点到2^j节点的最大差价
  5. down[i][j]表示2^j节点到i节点的最大差价

维护完这五个参数之后,查询x 和 y两点的最大差价求出 x -> LCA的最大差价(ans1),x -> LCA的最小权值(min_ans),y - > LCA的最大差价(ans2),y - > LCA的最大权值(max_ans)。最后得到答案就是 max(ans1,ans2),max(max_ans-min_ans,0)四个答案里面最大的那个。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int inf = 0x3f3f3f3f; 
const int maxn = 50005;
const int maxd = 19;
struct edge{
	int to;
	int next;
}e[maxn<<1];
int a[maxn];
int dep[maxn];
int head[maxn];
int maxv[maxn][20];
int minv[maxn][20];
int up[maxn][20];
int down[maxn][20];
int f[maxn][20];
int tot;
inline void clear_set()
{
	memset(head,-1,sizeof(head));
	memset(f,0,sizeof(f));
	memset(dep,0,sizeof(dep));
	memset(maxv,0,sizeof(maxv));
	memset(minv,0,sizeof(minv));
	memset(up,0,sizeof(up));
	memset(down,0,sizeof(down));
}
inline void addedge(int x,int y)
{
	e[tot].to = y;
	e[tot].next = head[x];
	head[x] = tot++;
}
inline void dfs(int x,int fx)
{
	for(int i = head[x];~i;i = e[i].next){
		int y = e[i].to;
		if(y == fx)		continue;
		dep[y] = dep[x] + 1;
		f[y][0] = x;					
		minv[y][0] = min(a[x],a[y]);	
		maxv[y][0] = max(a[x],a[y]);
		up[y][0] = max(0,a[x]-a[y]);
		down[y][0] = max(0,a[y]-a[x]);
		dfs(y,x); 
	}
}
int LCA(int x,int y)
{
	if(dep[x] < dep[y])		swap(x,y);
	int d = dep[x] - dep[y];
	for(int i = 0;i <= maxd;i++){
		if(((1<<i)&d)){
			x = f[x][i];
		}
	}
	if(x == y)		return x;
	for(int i = maxd;i >= 0;i--){
		if(f[x][i] != f[y][i]){
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}
int query_u(int x,int d,int &min_ans)
{
	int ans = 0;
	min_ans = inf;
	for(int i = 0;i <= maxd;i++){
		if(((1<<i)&d)){
			ans = max(ans,up[x][i]);
			ans = max(ans,maxv[x][i]-min_ans);
			min_ans = min(min_ans,minv[x][i]);
			x = f[x][i];
		}
	}
	return ans;				
} 
int query_d(int x,int d,int &max_ans)
{
	int ans = 0;
	max_ans = 0;
	for(int i = 0;i <= maxd;i++){
		if(((1<<i)&d)){
			ans = max(ans,down[x][i]);
			ans = max(ans,max_ans-minv[x][i]);
			max_ans = max(max_ans,maxv[x][i]);
			x = f[x][i];
		}
	}
	return ans;
} 
int main()
{
	int n,m;
	while(~scanf("%d",&n)){
		tot = 0;clear_set();
		for(int i = 1;i <= n;i++){
			scanf("%d",&a[i]);
		}
		int x,y;
		for(int i = 1;i < n;i++){
			scanf("%d%d",&x,&y);
			addedge(x,y);addedge(y,x);
		}
		dep[1] = 1;dfs(1,0);
		for(int j = 1;j <= maxd;j++){
			for(int i = 1;i <= n;i++){
				int fx = f[i][j-1];
				f[i][j] = f[fx][j-1];			
				maxv[i][j] = max(maxv[i][j-1],maxv[fx][j-1]);		//更新最大值 
				minv[i][j] = min(minv[i][j-1],minv[fx][j-1]);		//更新最小值 
				int a = max(up[i][j-1],up[fx][j-1]);				//[i,2^(j-1)],[fx,2^(j-1)]最大 
				int b = max(0,maxv[fx][j-1]-minv[i][j-1]);			//[fx,2^(j-1)]最大权 -  [i,2^(j-1)]最小 
				up[i][j] = max(a,b);								//up是维护i节点到 2^j节点的最大     (x -> LCA) 
				a = max(down[i][j-1],down[fx][j-1]);				//[i,2^(j-1)],[fx,2^(j-1)]最大
				b = max(0,maxv[i][j-1]-minv[fx][j-1]);				//[i,2^(j-1)]最大权 -  [fx,2^(j-1)]最小权
				down[i][j] = max(a,b);								//down是维护2^j节点到 i节点的最大   (LCA -> y) 
			}
		}
		scanf("%d",&m);
		while(m--){
			scanf("%d%d",&x,&y);
			int r = LCA(x,y);
			int min_ans,max_ans;
			int ans1 = query_u(x,dep[x]-dep[r],min_ans);			 
			int ans2 = query_d(y,dep[y]-dep[r],max_ans);
			int ans = max(max(ans1,ans2),max(max_ans-min_ans,0));
			printf("%d\n",ans);
		}
	}
	return 0;
}

愿你走出半生,归来仍是少年~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值