hdu 3078 Network (LCA转RMQ)

题意:n个结点形成一棵树,每个结点有一个权值,两种操作:一、改变某个结点的权值。二、查询结点u到结点v的路径上第k大的权值。


由于牵涉修改操作,因此不便用离线tarjan来做,这里采用LCA转RMQ的方法来求LCA。

关于LCA与RMQ的相互转换


建树之后DFS求出各个结点的深度dep、欧拉序列E、第一次在欧拉序列中出现的位置pos、以及前驱结点pre。

然后可转化为RMQ来查询两个结点的路径中深度最小的结点在欧拉序列中的下标,该结点就是这两个结点的最近公共祖先(LCA)。

得到LCA之后,通过前驱结点得到两个结点路径上经过的所有结点,然后排序,得到第k大的结点。


注意,RMQ查询的是欧拉序列的下标!!欧拉序列大小为2*n-1,初始化RMQ的时候,不要误将n作为欧拉序列大小。


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
#define maxn 80001
struct Edge{
    int to,next,w;
}edge[maxn<<1];

int a[maxn],head[maxn],stk[maxn<<1],pre[maxn],dep[maxn<<1],cnt,pos[maxn],E[maxn<<1],dfn,f[maxn<<1][20];

inline void add(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void init()
{
    memset(head,-1,sizeof(head));
    memset(pos,-1,sizeof(pos));
    memset(pre,-1,sizeof(pre));
    cnt=dfn=0;
}

void dfs(int u,int deep)
{
    if(pos[u]!=-1) return;
    E[dfn]=u,dep[dfn]=deep,pos[u]=dfn++;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(pos[v]==-1)
        {
            pre[v]=u;
            dfs(v,deep+1);
            E[dfn]=u,dep[dfn++]=deep;
        }
    }
}

void init_RMQ(int n)
{
    for(int i=1;i<=n;++i) f[i][0]=i;
    for(int j=1;(1<<j)<=n;++j)
        for(int i=1;i+(1<<j)-1<=n;++i)
        {
            if(dep[f[i][j-1]]<dep[f[i+(1<<(j-1))][j-1]]) f[i][j]=f[i][j-1];
            else f[i][j]=f[i+(1<<(j-1))][j-1];
        }
}

inline int RMQ(int L,int R)
{
    int k=0;
    while(1<<(k+1)<=R-L+1) ++k;
    if(dep[f[L][k]]<dep[f[R-(1<<k)+1][k]]) return f[L][k];
    return f[R-(1<<k)+1][k];
}

inline int lca(int u,int v)
{
    if(pos[u]>pos[v]) return E[RMQ(pos[v],pos[u])];
    return E[RMQ(pos[u],pos[v])];
}

bool cmp(int x,int y) {return x>y;}
void Find(int k,int u,int v)
{
    int fa=lca(u,v),tot=0;
    while(u!=fa){
        stk[tot++]=a[u];
        u=pre[u];
    }
    while(v!=fa){
        stk[tot++]=a[v];
        v=pre[v];
    }
    stk[tot++]=a[fa];
    if(tot<k) puts("invalid request!");
    else{
        sort(stk,stk+tot,cmp);
        printf("%d\n",stk[k-1]);
    }
}

int main()
{
    int n,i,u,v,k,q;
    while(~scanf("%d%d",&n,&q))
    {
        for(i=1;i<=n;++i) scanf("%d",&a[i]);
        init();
        for(i=1;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        pre[1]=1;
        dfs(1,0);
        init_RMQ(2*n-1);
        while(q--)
        {
            scanf("%d%d%d",&k,&u,&v);
            if(!k) a[u]=v;
            else Find(k,u,v);
        }
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值