poj3728 The merchant LCA+并查集

题意:有一种商品。在N个城市中的价格都不一样、给定N个点和N-1条边。有Q个询问。每个询问的内容为:

商人从u-->v这条路径上所能获得的最大利润是多少、商人只可以在某一个点买进东西。在这个点之后卖出它、



思路:

由于给定的是一颗简单树。很容易就想到可以用LCA来做。难点在于。怎么求出u-->t-->v的最大利润,

t==lca(u,v)

可以想到并查集的更新操作。

对于每一个询问的u和v 如果我知道以下内容:

1) u到最近的祖先的最大利润up[u]  和 最近祖先到u的最大利润 down[u]

2) u到最近祖先的最大值maxx[u]  和最小值 minx[u]

那么我们就可以很容易求出u-->v的最大利润了

u-->v的最大利润 = max(up[u],down[v],maxx[v]-minx[u])

但是主要的问题怎么解决

通过tarjan递归的时候结局 和 并查集中的Find来处理。

详情代码:


//author: CHC
//First Edit Time:	2014-08-02 21:59
//Last Edit Time:	2014-08-02 22:40
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;
#define MAXN 50010
struct Q
{
    int u,v,id,dir;
    Q(){}
    Q(int _u,int _v,int _id,int _dir):u(_u),v(_v),id(_id),dir(_dir){}
};
int path[MAXN];
vector <int> e[MAXN];
vector <Q> h[MAXN],q[MAXN];
int maxx[MAXN],minx[MAXN],up[MAXN],down[MAXN],n,ans[MAXN];
int vis[MAXN];
inline void init(){
    for(int i=0;i<=n+3;i++){
        path[i]=i;
        vis[i]=up[i]=down[i]=0;
        e[i].clear();
        h[i].clear();
        q[i].clear();
    }
}
int Find(int x){
    if(x==path[x])return x;
    int px=path[x];
    path[x]=Find(path[x]);
    up[x]=max(maxx[px]-minx[x],max(up[x],up[px]));
    down[x]=max(maxx[x]-minx[px],max(down[x],down[px]));
    maxx[x]=max(maxx[x],maxx[px]);
    minx[x]=min(minx[x],minx[px]);
    return path[x];
}
void LCA(int u){
    vis[u]=1;
    for(int i=0;i<(int)q[u].size();i++){
        Q &next=q[u][i];
        if(vis[next.v]){
            int lca=Find(next.v);
            h[lca].push_back(next);
        }
    }
    for(int i=0;i<(int)e[u].size();i++){
        int v=e[u][i];
        if(!vis[v]){
            LCA(v);
            path[v]=u;
        }
    }
    for(int i=0;i<(int)h[u].size();i++){
        Q now=h[u][i];
        Find(now.u); Find(now.v);
        if(!now.dir) swap(now.u,now.v);
        ans[now.id]=max(up[now.u],down[now.v]);
        ans[now.id]=max(ans[now.id],maxx[now.v]-minx[now.u]);
    }
}
int main()
{
    while(~scanf("%d",&n)){
        init();
        for(int i=1,x;i<=n;i++){
            scanf("%d",&x);
            maxx[i]=minx[i]=x;
        }
        for(int i=1,x,y;i<n;i++){
            scanf("%d%d",&x,&y);
            e[x].push_back(y);
            e[y].push_back(x);
        }
        int qq;
        scanf("%d",&qq);
        for(int i=0,x,y;i<qq;i++){
            scanf("%d%d",&x,&y);
            q[x].push_back(Q(x,y,i,1));
            q[y].push_back(Q(y,x,i,0));
        }
        LCA(1);
        for(int i=0;i<qq;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值