POJ 3728 【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.

1 ≤ N, wi, Q ≤ 50000


【Output Format】
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个节点的树,每个节点有一个点权,给Q个询问,每个询问给定两个节点编号u v,求在树上从u走向v的无重复路径中寻找两个节点p q使得p的遍历顺序在q之前且q点点权减去p点点权值最大,并输出那个最大值


树上倍增 lca
显然q点尽量大p点尽量小比较优
显然刚开始是想着从u走向lca(u,v),再从lca(u,v)走向v并从中选取两个符合要求的节点,操作比较简单 但是时间巨慢,所以开始想如何在树上倍增的同时寻找到符合要求的点
与倍增思想一致f[j][i]记录j的第2^i个祖先 f[j][i]=f[f[j][i-1]][i-1],
g[j][i][0]记录第j个节点到f[j][i]这个节点中的最小值 g[j][i][0]=min(g[j][i-1][0],g[f[j][i-1]][i-1][0]),
g[j][i][1]记录j到f[j][i]中的最大值 g[j][i][1]=max(g[j][i-1][1],g[f[j][i-1]][i-1][1])
为了做到快速寻找答案用
up[j][i]记录j到f[j][i]的路径中符合条件的最大值 up[j][i]=max(up[j][i-1],max(up[f[j][i-1]][i-1],g[f[j][i-1]][i-1][1]-g[j][i-1][0])),
down[j][i]记录f[j][i]到j的路径中符合条件的最大值 down[j][i]=max(down[j][i-1],max(down[f[j][i-1]][i-1],g[j][i-1][1]-g[f[j][i-1]][i-1][0])),
在做倍增查找lca的时候记录lca(u,v)到v的最大值Max=max(Max,g[y][i][1]),倍增时每段的最小值与前面的最大值做差与down以及ans比较
以及u到lca(u,v)的最小值Min=min(Min,g[x][i][0]),倍增时每段的最大值与前面的最小值做差与up以及ans比较
可以快速查找u到lca(u,v)及lca(u,v)到v的符合条件的最大值最后将Max-Min于ans比较
详见代码


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <map>
using namespace std;
int d[55000],f[55000][21],g[55000][21][2],a[55000],first[55000],up[55000][21],down[50005][21];
int i,x,y,j,k,l,m,n,Max,Min,num,ans;
struct info
  {
    int ar,next;
  }tree[100005];
void add(int x,int y)
  {
    tree[++num]=(info){y,first[x]};first[x]=num;
  }
void dfs(int u)
  {
    int i,v;
    for (i=first[u];i;i=tree[i].next)
      {
        v=tree[i].ar;
        if (d[v]) continue;
        d[v]=d[u]+1;f[v][0]=u;
        g[v][0][0]=min(a[u],a[v]);g[v][0][1]=max(a[u],a[v]);
        up[v][0]=max(0,a[u]-a[v]);down[v][0]=max(a[v]-a[u],0);
        dfs(v);
      }
  }
int main()
  {
    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);
    f[1][0]=1;g[1][0][0]=g[1][0][1]=a[1];d[1]=1;
    dfs(1);
    for (i=1;i<20;i++)
      for (j=1;j<=n;j++)
        {
           f[j][i]=f[f[j][i-1]][i-1];
           g[j][i][0]=min(g[j][i-1][0],g[f[j][i-1]][i-1][0]);
           g[j][i][1]=max(g[j][i-1][1],g[f[j][i-1]][i-1][1]);
           up[j][i]=max(up[j][i-1],max(up[f[j][i-1]][i-1],g[f[j][i-1]][i-1][1]-g[j][i-1][0]));
           down[j][i]=max(down[j][i-1],max(down[f[j][i-1]][i-1],g[j][i-1][1]-g[f[j][i-1]][i-1][0]));
        }
    for (scanf("%d",&m);m;m--)
      {
        scanf("%d%d",&x,&y);
        Min=a[x];Max=a[y];ans=0;
        for (;d[x]>d[y];)
          {
            for (i=0;d[f[x][i]]>=d[y];i++);i--;
            ans=max(ans,max(g[x][i][1]-Min,up[x][i]));
            Min=min(Min,g[x][i][0]);
            x=f[x][i];
          }
        for (;d[y]>d[x];)
          {
            for (i=0;d[x]<=d[f[y][i]];i++);i--;
            ans=max(ans,max(Max-g[y][i][0],down[y][i]));
            Max=max(Max,g[y][i][1]);
            y=f[y][i];
          }
        for (;x!=y;)
          {
            for (i=1;f[x][i]!=f[y][i];i++);i--;
            ans=max(ans,max(g[x][i][1]-Min,up[x][i]));
            ans=max(ans,max(Max-g[y][i][0],down[y][i]));
            Max=max(Max,g[y][i][1]);Min=min(Min,g[x][i][0]);
            x=f[x][i];y=f[y][i];
          }
        ans=max(ans,Max-Min);
        printf("%d\n",ans);
       } 
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值