[最近公共祖先] POJ 3728 The merchant

The merchant

Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 4556   Accepted: 1576

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 ≤ NwiQ ≤ 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

Source

POJ Monthly Contest – 2009.04.05, GaoYihan

做了这道题,有一种怀念的感觉,想到了高中时codevs上的一道神题,水果姐逛水果街(好像一模一样)。

不禁感慨万千,于是找到高中时用PASCAL打的程序,结果还A了,又是感慨万千。

下面看一下水果姐逛水果街的原题:

3305 水果姐逛水果街Ⅱ
时间限制: 2 s
空间限制: 256000 KB
题目等级 : 钻石 Diamond
题解
题目描述 Description
水果姐第二天心情也很不错,又来逛水果街。

突然,cgh又出现了。cgh施展了魔法,水果街变成了树结构(店与店之间只有一条唯一的路径)。

同样还是n家水果店,编号为1~n,每家店能买水果也能卖水果,并且同一家店卖与买的价格一样。

cgh给出m个问题,每个问题要求水果姐从第x家店出发到第y家店,途中只能选一家店买一个水果,然后选一家店(可以是同一家店,但不能往回走)卖出去。求最多可以赚多少钱。

水果姐向学过oi的你求助。

输入描述 Input Description
第一行n,表示有n家店

下来n个正整数,表示每家店一个苹果的价格。

下来n-1行,每行两个整数x,y,表示第x家店和第y家店有一条边。

下来一个整数m,表示下来有m个询问。

下来有m行,每行两个整数x和y,表示从第x家店出发到第y家店。

输出描述 Output Description
有m行。

每行对应一个询问,一个整数,表示面对cgh的每次询问,水果姐最多可以赚到多少钱。

样例输入 Sample Input
10
16 5 1 15 15 1 8 9 9 15
1 2
1 3
2 4
2 5
2 6
6 7
4 8
1 9
1 10
6
9 1
5 1
1 7
3 3
1 1
3 6

样例输出 Sample Output
7
11
7
0
0
15

数据范围及提示 Data Size & Hint
0<=苹果的价格<=10^8

0<n<=200000

0<m<=10000

是不是一模一样?

言归正传,题目给出后很明显是一个树形结构,而且要途径起点和目标点的最近公共祖先。

那么,如何才能顺序求出两点间的最大差呢?

我们可以想一下两点间的最大差有多少种情况。

①公共祖先到终点的最大值-起点到公共祖先的最小值

②起点到公共祖先的最大值-之后起点到公共祖先的最小值

③公共祖先到终点的最大值-之后终点到公共祖先的最小值

除了以上三种,就没有其他情况了。

其中的顺序问题比较难处理,我们可以求出公共祖先后,对起点和终点分别遍历,求出起点边的最小值和终点边的最大值,①就算解决了。

对于顺序问题,可以开四个数组,max[i][j],min[i][j]代表i到2^j的最大值和最小值。

sx[i][j],dx[i][j]代表i到2^j的顺序差的最大值,倒序差的最大值(顺序是叶子结点-根节点方向,倒序相反,由自己喜欢而定)

于是处理的过程为:

ancestor[i][j].v=ancestor[ancestor[i][j-1].v][j-1].v;
ancestor[i][j].max=max(ancestor[i][j-1].max,ancestor[ancestor[i][j-1].v][j-1].max);
ancestor[i][j].min=min(ancestor[i][j-1].min,ancestor[ancestor[i][j-1].v][j-1].min);
ancestor[i][j].sx=max(max(ancestor[i][j-1].sx,ancestor[ancestor[i][j-1].v][j-1].sx),ancestor[i][j-1].max-ancestor[ancestor[i][j-1].v][j-1].min);
ancestor[i][j].dx=max(max(ancestor[i][j-1].dx,ancestor[ancestor[i][j-1].v][j-1].dx),ancestor[ancestor[i][j-1].v][j-1].max-ancestor[i][j-1].min);

这样就可以有顺序的判断了。

附上代码:

C风格C++版

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct mp
  {
  	 int next,to;
  }map[211010];
struct anc
  {
  	 int v,max,min,dx,sx;
  }ancestor[151010][18];
int w[211010],frist[211010],num,dep[211010],n;
void init()
  {
  	 num=0;
  	 memset(frist,0,sizeof(frist));
  	 memset(map,0,sizeof(map));
  	 memset(ancestor,0,sizeof(ancestor));
  	 memset(w,0,sizeof(w));
  	 memset(dep,0,sizeof(dep));
  }
void add(int x,int y)
  {
  	 ++num;
  	 map[num].to=y;
  	 map[num].next=frist[x];frist[x]=num;
  }
void build(int v)
  {
  	 int i;
  	 for(i=frist[v];i;i=map[i].next)
  	   {
  	   	 if(!dep[map[i].to])
  	   	   {
  	   	   	  ancestor[map[i].to][0].v=v;
  	   	   	  ancestor[map[i].to][0].max=max(w[map[i].to],w[v]);
  	   	   	  ancestor[map[i].to][0].min=min(w[map[i].to],w[v]);
  	   	   	  ancestor[map[i].to][0].sx=w[map[i].to]-w[v];
  	   	   	  ancestor[map[i].to][0].dx=w[v]-w[map[i].to];
  	   	   	  dep[map[i].to]=dep[v]+1;
  	   	   	  build(map[i].to);
		   }
	   }
  }
void init_ancestor()
  {
  	 int i,j;
  	 for(j=1;j<18;++j)
  	   for(i=1;i<=n;++i)
  	     if(ancestor[i][j-1].v)
  	       {
  	         ancestor[i][j].v=ancestor[ancestor[i][j-1].v][j-1].v;
  	         ancestor[i][j].max=max(ancestor[i][j-1].max,ancestor[ancestor[i][j-1].v][j-1].max);
  	         ancestor[i][j].min=min(ancestor[i][j-1].min,ancestor[ancestor[i][j-1].v][j-1].min);
  	         ancestor[i][j].sx=max(max(ancestor[i][j-1].sx,ancestor[ancestor[i][j-1].v][j-1].sx),ancestor[i][j-1].max-ancestor[ancestor[i][j-1].v][j-1].min);
  	         ancestor[i][j].dx=max(max(ancestor[i][j-1].dx,ancestor[ancestor[i][j-1].v][j-1].dx),ancestor[ancestor[i][j-1].v][j-1].max-ancestor[i][j-1].min);
		   }
  }
int lca(int x,int y)
  {
  	 int i,deep;
  	 if(dep[x]<dep[y]) swap(x,y);
  	 deep=dep[x]-dep[y];
  	 for(i=0;i<18;++i) if((1<<i)&deep) x=ancestor[x][i].v;
  	 if(x==y) return x;
  	 for(i=17;i>=0;--i)
  	   {
  	   	  if(ancestor[x][i].v!=ancestor[y][i].v)
  	   	    {
  	   	      x=ancestor[x][i].v;
  	   	      y=ancestor[y][i].v;
			}
	   }
	 return ancestor[x][0].v;
  }
int main()
  {
  	 int i,v,w1,z,q,qa,qb,deep,ans=0,maxx,minx;
  	 init();
  	 scanf("%d",&n);
  	 for(i=1;i<=n;++i) scanf("%d",&w[i]);
  	 ancestor[1][0].max=ancestor[1][0].min=ancestor[1][0].dx=ancestor[1][0].sx=w[1];
  	 for(i=0;i<n-1;++i)
  	   {
  	   	  scanf("%d%d",&v,&w1);
  	   	  add(v,w1);add(w1,v);
	   }
	 dep[1]=1;
	 build(1);
	 init_ancestor();
	 scanf("%d",&q);
	 while(q--)
	   {
	   	  scanf("%d%d",&qa,&qb);
	   	  z=lca(qa,qb);
	   	  if(qa==qb)
	   	    {
	   	      printf("0\n");
	   	      continue;
			}
	   	  deep=dep[qa]-dep[z];
	   	  maxx=-1;minx=1e9;ans=0;
	   	  if(deep>0)
	   	    for(i=0;i<18;++i)
	   	      if((1<<i)&deep)
	   	        {
	   	          ans=max(ans,ancestor[qa][i].dx);
	   	          ans=max(ans,ancestor[qa][i].max-minx);
	   	          minx=min(minx,ancestor[qa][i].min);
	   	          qa=ancestor[qa][i].v;
			    }
		  deep=dep[qb]-dep[z];
		  if(deep>0)
	   	    for(i=0;i<18;++i)
	   	      if((1<<i)&deep)
	   	        {
	   	          ans=max(ans,ancestor[qb][i].sx);
	   	          ans=max(ans,maxx-ancestor[qb][i].min);
	   	          maxx=max(maxx,ancestor[qb][i].max);
	   	          qb=ancestor[qb][i].v;
			    }
		  ans=max(maxx-minx,ans);
		  printf("%d\n",ans);
	   }
  	 return 0;
  }

 Pascal版

const
sd:array[0..15] of longint=(1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,16384<<1);
var  n,i,x,y,e,k,q,ans,maxx,mixx,z,t,j:longint;
     cost,node,next,h,hi:array[-100..2050000] of longint;
     v:array[-100..2005000] of boolean;
     f,maxn,minn,sx,dx:array[-100..200050,0..50] of longint;
function max(a,b:longint):longint;
  begin
    if a>b then exit(a) else exit(b);
  end;
function min(a,b:longint):longint;
  begin
    if a>b then exit(b) else exit(a);
  end;
procedure swap(var a,b:longint);
  begin
    if a=b then exit;
    a:=a xor b;b:=a xor b;a:=a xor b;
  end;
procedure add(a,b:longint);
  begin
    inc(e);
    next[e]:=h[a];h[a]:=e;
    node[e]:=b;
  end;
procedure built(x,hig:longint);
 var p:longint;
  begin
    v[x]:=true;
    p:=h[x];
    hi[x]:=hig;
    while p>0 do
      begin
        if not(v[node[p]]) then
          begin
            f[node[p],0]:=x;
            maxn[node[p],0]:=max(cost[x],cost[node[p]]);
            minn[node[p],0]:=min(cost[x],cost[node[p]]);
            sx[node[p],0]:=cost[node[p]]-cost[x];
            dx[node[p],0]:=cost[x]-cost[node[p]];
            built(node[p],hig+1);
          end;
        p:=next[p];
      end;
  end;
function lca(a,b:longint):longint;
  begin
    lca:=0;
    if hi[a]<hi[b] then swap(a,b);
    while hi[a]>hi[b] do
      begin
        k:=0;
        while hi[a]-hi[b]>sd[k+1] do inc(k);
        a:=f[a,k];lca:=a;
      end;
    while a<>b do
      begin
        if f[a,0]=f[b,0] then exit(f[a,0]);
        k:=0;
        while (f[a,k+1]<>f[b,k+1]) and (hi[a]>=sd[k+1]) do inc(k);
        a:=f[a,k];b:=f[b,k];lca:=a;
      end;
  end;
procedure init;
  begin
  readln(n);
  for i:=1 to n do read(cost[i]);
  maxn[1,0]:=cost[1];minn[1,0]:=cost[1];
  sx[1,0]:=cost[1];dx[1,0]:=cost[1];
  for i:=1 to n-1 do
    begin
      readln(x,y);
      add(x,y);add(y,x);
    end;
   built(1,0);
   k:=1;
   while sd[k]<n do
     begin
       for i:=1 to n do
         begin
           if hi[i]>=sd[k] then
             begin
                f[i,k]:=f[f[i,k-1],k-1];
                maxn[i,k]:=max(maxn[f[i,k-1],k-1],maxn[i,k-1]);
                minn[i,k]:=min(minn[f[i,k-1],k-1],minn[i,k-1]);
                dx[i,k]:=max(max(dx[i,k-1],dx[f[i,k-1],k-1]),maxn[f[i,k-1],k-1]-minn[i,k-1]);
                sx[i,k]:=max(max(sx[i,k-1],sx[f[i,k-1],k-1]),maxn[i,k-1]-minn[f[i,k-1],k-1]);
           end;
         end;
         inc(k);
     end;
  readln(q);
end;
begin
  init;
  for i:=1 to q do
    begin
      maxx:=0;mixx:=100000000;ans:=0;
      readln(x,y);
      if x=y then
        begin
          writeln(0);
          continue;
        end;
      z:=lca(x,y);
      t:=hi[x]-hi[z];
      if t>0 then
          begin
             while hi[x]>hi[z] do
               begin
                  k:=0;
                  while hi[x]-hi[z]>sd[k+1] do inc(k);
                     ans:=max(dx[x,k],ans);
                     ans:=max(ans,maxn[x,k]-mixx);
                     mixx:=min(mixx,minn[x,k]);
                     x:=f[x,k];
                  end;
          end;
        t:=hi[y]-hi[z];
        if t>0 then
          begin
                while hi[y]>hi[z] do
                  begin
                  k:=0;
                  while hi[y]-hi[z]>sd[k+1] do inc(k);
                   ans:=max(sx[y,k],ans);
                    ans:=max(ans,maxx-minn[y,k]);
                    maxx:=max(maxx,maxn[y,k]);
                    y:=f[y,k];
                    end;

          end;
              ans:=max(maxx-mixx,ans);
              writeln(ans);
    end;
end.

 时间比较:

 

明显转了C++快多了,不过也为以前的自己点赞。

时间: 2024-10-12 04:15:42

[最近公共祖先] POJ 3728 The merchant的相关文章

[最近公共祖先] POJ 1330 Nearest Common Ancestors

Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 27316   Accepted: 14052 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:  In the figure, eac

poj 3728 The merchant(LCA)

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 on

poj——3728 The merchant

The merchant Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 5055   Accepted: 1740 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 w

POJ - 3728 The merchant(dp+LCA)

题目大意:给出N个点,和每个点物品的售价,现在有一个商人,要从u点到v点,他想在路上多赚点钱.他可以从一个城市买物品,然后再卖到另一个城市,但买卖只允许一次,且不能回头走 问最多能赚多少 解题思路:果然智商捉急了.. up数组纪录当前点到lca的最大利润 down数组纪录lca到当前点的最大利润 Max数组lca到当前点的最大值 Min纪录当前点到lca的最小值 这样的话,执行tarjan的时候,就可以更新一下这些值了 首先,答案的话肯定是max(max(up, down), Max - Min

LCA 算法学习 (最近公共祖先)poj 1330

poj1330 在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点.这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共祖先只有一个.时间复杂度为O(n+q

POJ 1470 Closest Common Ancestors【最近公共祖先LCA】

题目链接:http://poj.org/problem?id=1470 题目大意:给出一棵树,再给出若干组数(a,b),输出节点a和节点b的最近公共祖先(LCA) 就是很裸的LCA,但是我用的是<挑战程序设计竞赛>上的"基于二分搜索的算法求LCA",我看网上用的都是tarjan算法.但是我的代码不知道为什么提交上去 wrong answer,自己想的很多测试数据也都和题解结果一样,不知道错在哪里,所以把代码保存一下,留待以后解决...... 如果读者有什么建议,希望提出来,

【POJ 1330】Nearest Common Ancestors(最近公共祖先)

传送门:http://poj.org/problem?id=1330 题意:很裸的最近公共祖先,看题就知道…模板题. 代码: /* *********************************************** Author :Torrance_ZHANG Created Time :2016/4/29 22:11:34 File Name :ceshi2.cpp ************************************************ */ #inclu

POJ 1330 LCA最近公共祖先 离线tarjan算法

题意要求一棵树上,两个点的最近公共祖先 即LCA 现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出 原理用了并查集和dfs染色,先dfs到底层开始往上回溯,边并查集合并 一边染色,这样只要询问的两个点均被染色了,就可以输出当前并查集的最高父亲一定是LCA,因为我是从底层层层往上DSU和染色的,要么没被染色,被染色之后,肯定就是当前节点是最近的 #include <iostream> #include <

POJ 1330 最近公共祖先LCA(Tarjan离线做法)

题目链接:http://poj.org/problem?id=1330 题目大意十分明了简单,就是给定一棵树,求某两个子节点的最近公共祖先,如果尚不清楚LCA的同学,可以左转百度等进行学习. 稍微需要注意的是,建树顺序需要按照题目给定的顺序进行,也就是说根被设定成第一个给出的结点,如样例2. 此题网上题解颇多,但是多是使用的邻接表存图,于是我这里采用了边表,不过实质上Tarjan的部分思想都是一样的,均利用了并查集. AC代码: #include <cstdio> #include <c