【POJ3728】The merchant

8 篇文章 0 订阅
4 篇文章 0 订阅

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 Format

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 .
1N,wi,Q50000

  • Output Format

The output contains Q <script type="math/tex" id="MathJax-Element-2404">Q</script> 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

  • Hint

【题目大意】
给你一颗有点权的树,若干询问,每次询问从u走到v路上,买卖一次东西所赚的最大差价为多少。


  • 分析

可以借助点分治的思想,最大差值要么在左链,要么在右链,要么穿过最近公共祖先。所以我们可以用倍增维护最大值,最小值,从上到下的最大差值,从下到上的最大差值。对于询问走一遍两点间的路径,答案为左链最大差值,右链最大差值,和右链最大值减左链最小值三者最小值。注意:左链的最大差值应该为每次倍增所得到的从下到上的最大差值和这段里的最大值减去这个点以下的最小值。(详见代码)


#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 50001
using namespace std;
int u,v,n,m,tot,last[N],Deep[N],Father[N][20],Max[N][20],Min[N][20],DeltaUp[N][20],DeltaDown[N][20],A[N];
struct Data{int to,next;}E[2*N];
void Addline(int u,int v){
    E[++tot].to=v; E[tot].next=last[u]; last[u]=tot;
    E[++tot].to=u; E[tot].next=last[v]; last[v]=tot;
}
void Dfs(int fa,int u){
    Deep[u]=Deep[fa]+1; Father[u][0]=fa;
    Max[u][0]=max(A[u],A[fa]); Min[u][0]=min(A[u],A[fa]);
    DeltaUp[u][0]=max(0,A[fa]-A[u]); DeltaDown[u][0]=max(0,A[u]-A[fa]);
    for (int i=1;i<=17;i++){
        Father[u][i]=Father[Father[u][i-1]][i-1];
        Max[u][i]=max(Max[u][i-1],Max[Father[u][i-1]][i-1]);
        Min[u][i]=min(Min[u][i-1],Min[Father[u][i-1]][i-1]);
        DeltaUp[u][i]=max(Max[Father[u][i-1]][i-1]-Min[u][i-1],max(DeltaUp[u][i-1],DeltaUp[Father[u][i-1]][i-1]));
        DeltaDown[u][i]=max(Max[u][i-1]-Min[Father[u][i-1]][i-1],max(DeltaDown[u][i-1],DeltaDown[Father[u][i-1]][i-1]));
    }
    for (int i=last[u];i;i=E[i].next){
        if (fa==E[i].to) continue;
        Dfs(u,E[i].to);
    }
}
int Lca(int u,int v){
    if (Deep[v]>Deep[u]) swap(u,v);
    for (int i;Deep[u]>Deep[v];u=Father[u][i-1])
        for (i=1;i<=17 && Deep[Father[u][i]]>=Deep[v];i++);
    for (int i;u!=v;u=Father[u][i-1],v=Father[v][i-1])
        for (i=1;i<=17 && Father[u][i]!=Father[v][i];i++);
    return u;
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&A[i]);
    for (int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        Addline(u,v);
    }
    for (int i=0;i<=17;i++) Max[1][i]=Min[1][i]=A[1],Father[1][i]=1;
    for (int i=last[1];i;i=E[i].next) Dfs(1,E[i].to);;
    scanf("%d",&m);
    for (int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        int fa=Lca(u,v),MaxDelta=0,Maxv=0,Minu=1<<30;
        for (int j;u!=fa;u=Father[u][j-1]){
            for (j=1;j<=17 && Deep[Father[u][j]]>=Deep[fa];j++);
            MaxDelta=max(MaxDelta,max(DeltaUp[u][j-1],Max[u][j-1]-Minu));
            Minu=min(Minu,Min[u][j-1]);
        }
        for (int j;v!=fa;v=Father[v][j-1]){
            for (j=1;j<=17 && Deep[Father[v][j]]>=Deep[fa];j++);
            MaxDelta=max(MaxDelta,max(DeltaDown[v][j-1],Maxv-Min[v][j-1]));
            Maxv=max(Maxv,Max[v][j-1]);
        }
        printf("%d\n",max(MaxDelta,Maxv-Minu));
    }
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值