1. 程式人生 > >POJ3728The merchant (倍增)(LCA)(DP)(經典)(||並查集壓縮路徑?)

POJ3728The merchant (倍增)(LCA)(DP)(經典)(||並查集壓縮路徑?)

城市 contain 分析 之間 pat rst .com span 題解

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個節點的樹,1<=N<=50000 每個節點都有一個權值,代表商品在這個節點的價格。商人從某個節點a移動到節點b(規定了方向),且只能購買並出售一次商品(出售在購買之後),問最多可以產生多大的利潤。

算法分析:

  • 顯然任意兩個城市之間的路徑是唯一的,商人有方向地從起點移動到終點。詢問這條路徑上任意兩點權值之差最大為多少,且要保證權值較大的節點在路徑上位於權值較小的節點之後。
  • 暴力的方法是顯而易見的,只要找到兩個點的深度最深的公共祖先,就等於找到了這條路徑,之後沿著路徑走一遍即可找到最大的利潤,然而無法滿足50000的數據規模。
  • 首先考慮高效尋找LCA(公共祖先)的方法。記錄ance[i][j]為節點i向上走2^j步到達的某個祖先。可以簡單地列出方程 ance[i][j]=ance[ance[i][j-1]][j-1];於是找到了高效構建的方法。
  • 每次尋找LCA 首先將兩個節點通過swim(a,b)函數轉移到同一深度,然後每次找一個最小的j使得ance[a][j]==ance[b][j] 之後將節點a賦值為ance[a][j-1] 直到j=0就找到了兩者的LCA
  • 現在我們已經找到了高效尋找LCA的方法,假設我們知道節點a到LCA的最小值minp[],LCA到節點b的最大值maxp[],
  • 以及買賣地點全在LCA之前可以獲得的最大利潤maxi[] 以及買賣地點全在LCA之後可以獲得的最大利潤maxI[] 顯然就得到了最後的答案。 維護這些數據的方式類似於維護ance數組的方式,DP方程也很好列出, 這裏就不給出了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=50010;
int dep[maxn],ance[maxn][17],maxp[maxn][17],minp[maxn][17],maxi[maxn][17];
int maxI[maxn][17],price[maxn],n,fa[maxn];
int q[maxn],head,tail,Laxt[maxn<<1],Next[maxn<<1],To[maxn<<1],cnt; 
int max(int a,int b) { if(a>b) return a; return b;}
int min(int a,int b) { if(a>b) return b; return a;}
void add(int u,int v)
{
    Next[++cnt]=Laxt[u];
    Laxt[u]=cnt;
    To[cnt]=v;
}
void BFS_build()
{
     q[++tail]=1;
     fa[1]=1;
     dep[1]=1;
     while(tail>head){
        int np=q[++head];
        ance[np][0]=fa[np];
        maxp[np][0]=max(price[np],price[fa[np]]);
        minp[np][0]=min(price[np],price[fa[np]]);
        if(price[np]<price[fa[np]]) maxi[np][0]=price[fa[np]]-price[np];
          else maxi[np][0]=0;
        if(price[np]>price[fa[np]]) maxI[np][0]=price[np]-price[fa[np]];
          else maxI[np][0]=0;
        for(int i=1;i<=16;i++){     //倍增DP方程
           
            ance[np][i]=ance[ance[np][i-1]][i-1];
            maxp[np][i]=max(maxp[np][i-1],maxp[ance[np][i-1]][i-1]);
            minp[np][i]=min(minp[np][i-1],minp[ance[np][i-1]][i-1]);
           
            int a=maxi[np][i-1],b=maxi[ance[np][i-1]][i-1];
            int c=maxp[ance[np][i-1]][i-1]-minp[np][i-1];
            maxi[np][i]=max(max(a,b),c);
            a=maxI[np][i-1]; b=maxI[ance[np][i-1]][i-1];c;
            c=maxp[np][i-1]-minp[ance[np][i-1]][i-1];
            maxI[np][i]=max(max(a,b),c);
            if(ance[np][i]==1)  break;
        }
       for(int i=Laxt[np];i;i=Next[i]){
           int nv=To[i];
           if(dep[nv])  continue;
           fa[nv]=np;
           dep[nv]=dep[np]+1;
           q[++tail]=nv;//q.push(nv);
       }
     }
}
int ia,ib,mi,ma;
int ancest;
void swim(int &a,int &b)
{
     if(dep[a]==dep[b])return ;
     while(dep[a]>dep[b]){
        int i;
        for(i=0;i<=16;i++) if(pow(2,i)+dep[b]>dep[a]) break;
        ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);
        mi=min(mi,minp[a][i-1]);
        a=ance[a][i-1];
    }
    while(dep[a]<dep[b]){
        int i;
        for(i=0;i<=16;i++) if(pow(2,i)+dep[a]>dep[b])break;
        ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);
        ma=max(ma,maxp[b][i-1]);
        b=ance[b][i-1];
    }
}
int solve(int a,int b)
{
    ia=0;ib=0;mi=price[a];ma=price[b];
    swim(a,b);
    if(a==b)return max(max(ia,ib),ma-mi); 
    while(true){  int i;
        for(i=0;i<=16;i++)
           if(ance[a][i]==ance[b][i])break;
           if(i==0){
              ancest=ance[a][0];
              ia=max(ia,price[ancest]-mi);
              ib=max(ib,ma-price[ancest]);
              mi=min(mi,price[ancest]);
              ma=max(ma,price[ancest]);
              return max(max(ia,ib),ma-mi);
           } else{
              ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);
              ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);
              mi=min(mi,minp[a][i-1]);
              ma=max(ma,maxp[b][i-1]);
              a=ance[a][i-1];b=ance[b][i-1];
            }
    }
}
int main()
{
     scanf("%d",&n);
     for(int i=1;i<=n;i++)
     scanf("%d",&price[i]);
     for(int i=1;i<n;i++){
         int a,b;
         scanf("%d%d",&a,&b);
          add(a,b); add(b,a);
     }
     BFS_build();
     int p;
     scanf("%d",&p);
     for(int i=1;i<=p;i++){
         int a,b;
         scanf("%d%d",&a,&b);
         printf("%d\n",solve(a,b));
     }
     return 0;
}

太球繁瑣了。。。。題解是COPY 過來的,代碼是稍微改了一下的。From:https://www.cnblogs.com/heisenberg-/p/5471202.html

然後並查集算法。。。emmm。等我智力夠了來。

POJ3728The merchant (倍增)(LCA)(DP)(經典)(||並查集壓縮路徑?)