POJ3728 The merchant(倍增LCA算法+树形DP)

题意:

给出N个点,和每个点物品的售价,现在有一个商人,要从u点到v点,他想在路上多赚点钱。他可以从一个城市买物品,然后再卖到另一个城市,但买卖只允许一次,且不能回头走 问最多能赚多少

题解:

对于一个询问, 假设u,v的LCA是f

有三种可能, 一个是从u到f 买卖了。 一个是从f到v买卖了,  一个是从u到f之间买了,从v到f卖了

在原有倍增数组的基础上,新开四个数组,分别表示:

dp_max:向上走2^k步之间的最高价格

dp_min:向上走2^k步之间的最低价格

dp_up:从u向上走2^k步的最大利润

dp_down:向下走2^k步到u的最大利润

然后对于每次询问,比较三个值:

从u到f买卖的最大利润

从f到v买卖的最大利润

从u到f买,从v到f卖的最大利润

俺又自闭了,智商跟不上...

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1e5;
int N;
int head[maxn];
int tol;
struct node {
    int u;
    int v;
    int next;
}edge[maxn];
void addedge (int u,int v) {
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol].next=head[u];
    head[u]=tol++;
} 
int weight[maxn];
int h[maxn];
int father[20][maxn];
int dp_max[20][maxn];//向上走2^k步之间的最高价格
int dp_min[20][maxn];//向上走2^k步之间的最低价格
int dp_up[20][maxn];//从u向上走2^k的最大利润
int dp_down[20][maxn];//向下走2^k步到u的最大利润 
void dfs (int x) {
    for (int i=head[x];i!=-1;i=edge[i].next) {
        int v=edge[i].v;
        if (v==father[0][x]) continue;
        father[0][v]=x;
        h[v]=h[x]+1;
        dp_up[0][v]=max(weight[x]-weight[v],0);
        dp_down[0][v]=max(weight[v]-weight[x],0);
        dp_max[0][v]=max(weight[v],weight[x]);
        dp_min[0][v]=min(weight[v],weight[x]);
        dfs(v);
    }
}
int lca (int x,int y) {
    if (h[x]<h[y]) swap(x,y);
    for (int i=17;i>=0;i--) 
        if (h[x]-h[y]>>i) x=father[i][x];
    if (x==y) return x;
    for (int i=17;i>=0;i--) 
        if (father[i][x]!=father[i][y]) {
            x=father[i][x];
            y=father[i][y];
        }
    return father[0][x];
}
int up (int u,int k,int &Min) {
    Min=1e9;
    int ans=0,preMin=1e9;
    for (int i=16;i>=0;i--) {
        if (k>>i&1) {
            Min=min(Min,dp_min[i][u]);
            ans=max(ans,dp_up[i][u]);
            ans=max(ans,dp_max[i][u]-preMin);
            preMin=min(preMin,dp_min[i][u]);
            u=father[i][u];
        }
    } 
    return ans;
}
int down (int u,int k,int &Max) {
    Max=0;
    int ans=0,preMax=0;
    for (int i=16;i>=0;i--) {
        if (k>>i&1) {
            Max=max(Max,dp_max[i][u]);
            ans=max(ans,dp_down[i][u]);
            ans=max(ans,preMax-dp_min[i][u]);
            preMax=max(preMax,dp_max[i][u]);
            u=father[i][u];
        }
    }
    return ans;
}
int main () {
    scanf("%d",&N);
    for (int i=1;i<=N;i++)
        scanf("%d",&weight[i]);
    memset(head,-1,sizeof(head));
    memset(dp_max,0,sizeof(dp_max));
    memset(dp_min,0x3f,sizeof(dp_min));
    tol=0;
    for (int i=0;i<N-1;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    int q;
    dfs(1);
    for (int i=1;i<=17;i++) 
        for (int j=1;j<=N;j++) {
            father[i][j]=father[i-1][father[i-1][j]];
            int mid=father[i-1][j];
            dp_max[i][j]=max(dp_max[i-1][j],dp_max[i-1][mid]);
            dp_min[i][j]=min(dp_min[i-1][j],dp_min[i-1][mid]);
            dp_up[i][j]=max(max(dp_up[i-1][j],dp_up[i-1][mid]),dp_max[i-1][mid]-dp_min[i-1][j]);
            dp_down[i][j]=max(max(dp_down[i-1][j],dp_down[i-1][mid]),dp_max[i-1][j]-dp_min[i-1][mid]);
        }
    scanf("%d",&q);
    for (int i=1;i<=q;i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        int Min=1e9,Max=-1;
        int r=lca(x,y);
        int up_w=up(x,h[x]-h[r],Min);
        int down_w=down(y,h[y]-h[r],Max);
        printf("%d\n",max(max(up_w,down_w),Max-Min));
    }
}

 

posted @ 2020-03-20 11:25  zlc0405  阅读(159)  评论(0编辑  收藏  举报