POJ - 3728 The merchant【离线LCA[倍增法]】

POJ - 3728传送


题意:给定n个城市,每两个城市之间有且仅有一条路。货物在各个城市的价格不同,在一条路径中,

       商人选择其 中一个城市买进货物,然后在之后的一个城市卖出货物,求所能获得的最大利润。
          对于q次询问,每次询问给定两个整数表示两个城市,输出两城市之间路径所对应的最大利润。
      注意最多只能买一次,然后卖一次。

思路:利用LCA倍增法求解过程,同时求出最大利润。

            在LCA倍增法中,设fa[x][i]表示,x点的2^i的祖宗节点。

            同理设up[x][i]为从x走到fa[x][i]过程能获得的最大利益

                设down[x][i]为从fa[x][i]走到x过程的最大利益

                设fma[x][i]为x和fa[x][i]之间的最大价格

                设fmi[x][i]为x和fa[x][i]之间的最小价格

                在倍增法过程就能快速求解出最大利益

                           注意维护过程

#include<cstring>
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define fma fmaxxxx
const int INF=0x3f3f3f3f;
const int N=50500;
int dep[N],fa[N][20],fma[N][20],fmi[N][20],up[N][20],down[N][20],val[N];
int n,m;
vector<int>g[N];
void dfs(int x,int pa,int d)
{
    dep[x]=d;
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i];
        if(y==pa)continue;
        fa[y][0]=x;
        fma[y][0]=max(val[x],val[y]);
        fmi[y][0]=min(val[x],val[y]);
        up[y][0]=max(0,val[x]-val[y]);
        down[y][0]=max(0,val[y]-val[x]);
        dfs(y,x,d+1);
    }
}
void init()
{
    for(int j=1;j<=m;j++)
    {
        for(int i=1;i<=n;i++)
        {
            if(fa[i][j-1]<0){fa[i][j-1]=-1;continue;}
            fa[i][j]=fa[fa[i][j-1]][j-1];
            fma[i][j]=max(fma[i][j-1],fma[fa[i][j-1]][j-1]);
            fmi[i][j]=min(fmi[i][j-1],fmi[fa[i][j-1]][j-1]);
            up[i][j]=max(up[i][j-1],up[fa[i][j-1]][j-1]);
            up[i][j]=max(up[i][j],fma[fa[i][j-1]][j-1]-fmi[i][j-1]);
            down[i][j]=max(down[i][j-1],down[fa[i][j-1]][j-1]);
            down[i][j]=max(down[i][j],fma[i][j-1]-fmi[fa[i][j-1]][j-1]);
        }
    }
}
int LCA(int x,int y)
{
    int ans=0,mixx=val[x],maxx=val[y];
    if(dep[x]>dep[y])
    {
        for(int i=0;i<=m;i++)
        {
            if((dep[x]-dep[y])&(1<<i))
            {
                ans=max(ans,up[x][i]);
                ans=max(ans,fma[x][i]-mixx);
                mixx=min(mixx,fmi[x][i]);
                x=fa[x][i];
            }
        }
    }
    else
    {
        for(int i=0;i<=m;i++)
        {
            if((dep[y]-dep[x])&(1<<i))
            {
                ans=max(ans,down[y][i]);
                ans=max(ans,maxx-fmi[y][i]);
                maxx=max(maxx,fma[y][i]);
                y=fa[y][i];
            }
        }
    }
    if(x==y)
    {
        ;
    }
    else
    {
        for(int i=m;i>=0;i--)
        {
            if(fa[x][i]!=fa[y][i])
            {
                ans=max(ans,up[x][i]);
                ans=max(ans,fma[x][i]-mixx);
                mixx=min(mixx,fmi[x][i]);
                ans=max(ans,down[y][i]);
                ans=max(ans,maxx-fmi[y][i]);
                maxx=max(maxx,fma[y][i]);
                x=fa[x][i];
                y=fa[y][i];
            }
        }
    mixx=min(mixx,fmi[x][0]);
    maxx=max(maxx,fma[y][0]);
    }
    ans=max(ans,maxx-mixx);
    printf("%d\n",ans);
    return fa[x][0];
}
int main()
{
    int x,y;
    while(scanf("%d",&n)!=EOF)
    {
        m=0;
        for(int i=1;i<=n;i++)
            g[i].clear();
        while(n>=(1<<m))m++;
        for(int i=1;i<=n;i++)
            scanf("%d",&val[i]);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }
        fa[1][0]=-1;
        dfs(1,-1,1);
        init();
        int TAT;
        scanf("%d",&TAT);
        while(TAT--)
        {
            scanf("%d%d",&x,&y);
            int ret=LCA(x,y);
            //-cout<<ret<<"    **"<<endl;
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值