poj3728 The merchant LCA(带权并查集?)

     一个树有N个节点,每个节点可以买卖同一件物品,不同的节点价格也不相同,给出每个节点的价钱,Q次询问x,y对于每次询问求从x到y最大可以获取的利润。

     离线LCA+带权并查集。带权并查集这类的题实在没怎么做过...这题看别人代码都看了好久才看明白...和裸地LCA不同的地方就是这里每个点要维护四个变量,up[x]表示从x到祖先可以获取的最大利润,down[x]表示从祖先到x可以获取的最大利润,mx[x]表示从祖先到x最大的价格,mi[x]表示从祖先到x最小的价格,这四个值在找爸爸的时候更新。记录答案的时候也不太一样,在tarjan的时候,对于查询,先按LCA点把查询归类,在扫面完u的子树后,在去记录u的子树中,x,y的LCA位u的查询的答案,也就是u左侧,u右侧,经过u三种情况找个最大值了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=50500;
int g1[maxn],g2[maxn],g3[maxn];
int m,p,n,num[maxn];
int mx[maxn],mi[maxn],up[maxn],down[maxn],ans[maxn];
int f[maxn];
bool vis[maxn];
struct EDGE
{
    int f,to;
    int d,next;
}edge[maxn<<2];
int find(int u)
{
    if (u==f[u]) return f[u];
    int tmp=f[u];
    f[u]=find(f[u]);
    up[u]=max(max(up[u],up[tmp]),mx[tmp]-mi[u]);
    down[u]=max(max(down[u],down[tmp]),mx[u]-mi[tmp]);

    mx[u]=max(mx[u],mx[tmp]);
    mi[u]=min(mi[u],mi[tmp]);
    return f[u];
}
void tarjan(int u)
{
    f[u]=u;
    int v,tmp,t;
    for (int j=g1[u]; j!=-1; j=edge[j].next)
    {
        v=edge[j].to;
        if (!vis[v])
        {
            tarjan(v);
            f[v]=u;
        }
    }
    vis[u]=true;
    for (int j=g2[u]; j!=-1; j=edge[j].next)
    {
        v=edge[j].to;
        if (vis[v])
        {
            t=find(v);
            edge[p].d=j;
            edge[p].next=g3[t];
            g3[t]=p;
            p++;
        }
    }
    int x,y;
    for (int j=g3[u]; j!=-1; j=edge[j].next)
    {
        int kk=edge[j].d;
        t=edge[kk].d;
        x=edge[kk].f;
        y=edge[kk].to;
        find(x);
        if (t<0)
        {
            swap(x,y);
            t=-t;
        }
        ans[t]=max(max(up[x],down[y]),mx[y]-mi[x]);
    }
}
int main()
{
//    freopen("in.txt","r",stdin);
    while (~scanf("%d",&n))
    {

        for (int i=1; i<=n; i++)
        {
            scanf("%d",&m);
            mx[i]=mi[i]=m;
        }
        memset(vis,false,sizeof vis);
        memset(g1,-1,sizeof g1);
        memset(g2,-1,sizeof g2);
        memset(g3,-1,sizeof g3);
        p=0;
        int x,y,z;
        for (int i=1; i<n; i++)
        {
            scanf("%d%d",&x,&y);
            edge[p].f=x;
            edge[p].to=y;
            edge[p].next=g1[x];
            g1[x]=p;
            p++;

//            edge[p].f=y;
//            edge[p].to=x;
//            edge[p].next=g1[y];
//            g1[y]=p;
//            p++;
        }
        scanf("%d",&m);
        for (int i=1; i<=m; i++)
        {
            scanf("%d%d",&x,&y);

            edge[p].d=i;
            edge[p].f=x;
            edge[p].to=y;
            edge[p].next=g2[x];
            g2[x]=p;
            p++;

            edge[p].d=-i;
            edge[p].f=y;
            edge[p].to=x;
            edge[p].next=g2[y];
            g2[y]=p;
            p++;
        }
        tarjan(1);
        for (int i=1; i<=m; i++)
        cout<<ans[i]<<endl;
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值