POJ 3728 The merchant 【在线LCA+细节】

传送门

题意:

一个国家有N个城市,每对城市之间只有一条简单的道路。商人选择了一些道路,并希望在每条道路上赚尽可能多的钱。当他沿着一条道路移动时,他可以选择一个城市购买一些商品,然后在一个城市中出售。所有城市的商品都一样,但价格不同。现在,您的任务是计算每条路径上可能的最大利润。

解析:这道题可以用离线LCA+DP解决,也可以用在线LCA+ RMQ。我用的第二种方法,这道题主要是利用grand数组的变形。

maxn[ s ][ i ] 表示 s 到 s向上 2^i 个结点的最大值。
minn[ s ][ i ] 表示 s 到 s向上 2^i 个结点的最小值。

设变量 father =grand [ s ] [ i - 1]
up[ s ][ i ] 表示 s 到 s向上 2^i 个结点的可能存在的答案。
可以由 maxn[ father ][ i - 1] - minn[ s ][ i - 1] , up [ s ] [ i - 1] 和 up [ grand ][ i - 1] 来更新

down[ s ][ i ] 表示 s 到 s向上 2^i 个结点可能存在的答案。
可以由 maxn[ s ][ i - 1] - minn[ father ][ i - 1] , down [ s ] [ i - 1] 和 down [ grand ][ i - 1] 来更新

设 x =LCA( s , e )
我们最后的答案可能存在的三种情况。
① s -> x 这一条链上
② x -> e 这一条链上
③ max(x - >e) - min(s -> x)

注意点:
求解 s -> x 这条链上的答案的时候,需要求解 ( up 数组的最大值 ) 和 ( maxn数组和它后代的最小值的差值的绝对值 ) 的最大值。
同理求解 x - >e 这条链上的答案的时候,需要求解( down数组的最大值 ) 和 (minn数组和它后代最大值的差值的绝对值) 的最大值
记住 N=log2(n) 的值 > 15.....
附上代码:
 

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;

int n,q;
int value[50005];
struct node
{
    int e;
    int p;
} load[100005];
int head[50005],sign;

void add_edge(int s,int e)
{
    load[++sign]=node{e,head[s]};
    head[s]=sign;
}

int depth[50005];
int grand[50005][20],N;
int maxn[50005][20];
int minn[50005][20];
int up[50005][20];
int down[50005][20];

void dfs(int s,int pre)
{
    int father;
    for(int i=1; i<=N; i++)
    {
        father=grand[s][i-1];
        grand[s][i]=grand[father][i-1];
        maxn[s][i]=max(maxn[s][i-1],maxn[father][i-1]);
        minn[s][i]=min(minn[s][i-1],minn[father][i-1]);

        up[s][i]=max(max(up[s][i-1],up[father][i-1]),maxn[father][i-1]-minn[s][i-1]);
        down[s][i]=max(max(down[s][i-1],down[father][i-1]),maxn[s][i-1]-minn[father][i-1]);
    }
    for(int i=head[s]; i!=-1; i=load[i].p)
    {
        int e=load[i].e;
        if(e!=pre)
        {
            depth[e]=depth[s]+1;
            grand[e][0]=s;
            maxn[e][0]=max(value[e],value[s]);
            minn[e][0]=min(value[e],value[s]);
            up[e][0]=max(0,value[s]-value[e]);
            down[e][0]=max(0,value[e]-value[s]);
            dfs(e,s);
        }
    }
}

int get_lca(int a,int b)
{
    if(depth[a]>depth[b])
        swap(a,b);
    for(int i=N; i>=0; i--)
    {
        if(depth[b]>=depth[a]&&depth[grand[b][i]]>=depth[a])
            b=grand[b][i];
    }
    for(int i=N; i>=0; i--)
    {
        if(grand[a][i]!=grand[b][i])
        {
            a=grand[a][i];
            b=grand[b][i];
        }
    }
    return a==b?a:grand[b][0];
}

int max_up,max_down;
int MAX,MIN;

void find_up(int s,int e)
{
    for(int i=N;i>=0;i--)
    {
        if(depth[s]>=depth[e]&&depth[grand[s][i]]>=depth[e])
        {
            max_up=max(max(max_up,up[s][i]),maxn[s][i]-MIN);
            MIN=min(MIN,minn[s][i]);
            s=grand[s][i];
        }
    }
}

void find_down(int s,int e)
{
    for(int i=N;i>=0;i--)
    {
        if(depth[s]>=depth[e]&&depth[grand[s][i]]>=depth[e])
        {
            max_down=max(max(max_down,down[s][i]),MAX-minn[s][i]);
            MAX=max(MAX,maxn[s][i]);
            s=grand[s][i];
        }
    }
}
void init()
{
    N=log2(n);
    sign=0;
    memset(head,-1,sizeof(head));
    memset(depth,0,sizeof(depth));
    memset(grand,0,sizeof(grand));
    memset(maxn,0,sizeof(maxn));
    memset(minn,0,sizeof(minn));
    memset(up,0,sizeof(up));
    memset(down,0,sizeof(down));
    depth[0]=-1;
}

int main()
{
    int s,e;
    scanf("%d",&n);
    init();
    for(int i=1;i<=n;i++)
        scanf("%d",&value[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&s,&e);
        add_edge(s,e);
        add_edge(e,s);
    }
    dfs(1,-1);
    scanf("%d",&q);
    while(q--)
    {
        MAX=0;
        MIN=INF;
        max_up=max_down=0;
        scanf("%d %d",&s,&e);
        int x=get_lca(s,e);
        find_up(s,x);
        find_down(e,x);
        ///cout<<MAX<<"   "<<MIN<<"   "<<max_up<<"  "<<max_down<<endl;
        printf("%d\n",max(MAX-MIN,max(max_up,max_down)));
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值