zoukankan      html  css  js  c++  java
  • [最近公共祖先] 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

     
    做了这道题,有一种怀念的感觉,想到了高中时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
    ");
    	   	      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
    ",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++快多了,不过也为以前的自己点赞。

  • 相关阅读:
    fort循环
    while
    函数和数组
    case
    init进程
    权限安全:堡垒机部署实践
    tcp首部当中seq和ack的增长规律
    VRRP
    MSTP
    字符集专题
  • 原文地址:https://www.cnblogs.com/fuermowei-sw/p/6172211.html
Copyright © 2011-2022 走看看