LCA——RMQ(线段树)

再学LCA,本来想学RMQ,结果只学会一半,另一半我用线段树写的。

首先,讲一下欧拉序,就是给一个树,从根节点开始,搜索到这个节点时,先在这个时候在序列中记录一下这个节点,然后依次搜索它的子节点,然后当从一个子节点回溯回来时,再在序列中记录一下这个节点,最后当这棵树都被搜索完后,所得到的序列就是这棵树的欧拉序。

然后就说一下这个运用线段树的RMQ的思路:

1.按上面的方式,求出这棵树的欧拉序,并记录每个节点在这个序列中的位置(每个节点只统计一个位置就行)。

2.用dfs求出每个节点的深度。

(上面的两个操作可以同时进行)

3.两个节点的最近公共祖先就是它们所在的最小子树的根节点,这个根节点也是这棵子树中深度最浅的节点,而最小子树的根节点在序列(欧拉序)中的初位置和末位置的中间部分就一定是这棵子树的节点,而这两个节点在序列的位置的中间部分也一定是这课子树上的节点,并且一定有这棵子树的根节点,现在把这个序列看做一棵线段树,两个节点的中间位置看做一个区间,在这个区间中查询这个根节点,即查询这个区间中深度最浅的节点。写一个线段树就可以了。

下面是代码:

#include<iostream>
#define MAXN 500001
using namespace std;
int read(){
    int x=0,f=1;
    char a=getchar();
    while(a<'0'||a>'9'){
        if(a=='-')f=-1;
        a=getchar();
    }
    while(a>='0'&&a<='9'){
        x*=10;
        x+=a-'0';
        a=getchar();
    }
    return x*f;
}
int n,m,s;
int head[500010],tot;
int a[1000010],cnt;
int h[500010],vis[500010];
int dian[500010];
struct node{
    int to;
    int nxt;
}edge[1000010];
struct nod{
    int l,r;
    int minn;
    int jie;
}tree[2000010];
void buildbiao(int u,int v){
    edge[++tot].to=v;
    edge[tot].nxt=head[u];
    head[u]=tot;
}
void dfs(int u){
    a[++cnt]=u;
    dian[u]=cnt;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(vis[v]==0){
            vis[v]=1;
            h[v]=h[u]+1;
            dfs(v);
            a[++cnt]=u;
        }
    }
}
void build(int i,int l,int r){
    tree[i].l=l;
    tree[i].r=r;
    if(l==r){
        tree[i].minn=h[a[l]];
        tree[i].jie=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(i*2,l,mid);
    build(i*2+1,mid+1,r);
    if(tree[i*2].minn<tree[i*2+1].minn){
        tree[i].minn=tree[i*2].minn;
        tree[i].jie=tree[i*2].jie;
    }
    else{
        tree[i].minn=tree[i*2+1].minn;
        tree[i].jie=tree[i*2+1].jie;
    }
}
nod search(int i,int l,int r){
    if(tree[i].l>=l&&tree[i].r<=r){
        return tree[i];
    }
    nod s1,s2;
    s1.minn=MAXN;
    s2.minn=MAXN;
    if(tree[i*2].r>=l){
        s1=search(i*2,l,r);
    }
    if(tree[i*2+1].l<=r){
        s2=search(i*2+1,l,r);
    }
    if(s1.minn>=s2.minn){
        s1=s2;
    }
    return s1;
}
int main(){
    n=read();m=read();s=read();
    for(int i=1;i<n;i++){
        int u,v;
        u=read();v=read();
        buildbiao(u,v);
        buildbiao(v,u);
    }
    vis[s]=1;
    h[s]=1;
    dfs(s);
    build(1,1,2*n-2);
    for(int i=1;i<=m;i++){
        int u,v;
        u=read();v=read();
        printf("%d\n",search(1,min(dian[u],dian[v]),max(dian[u],dian[v])).jie);
    }
    return 0;
}

 

posted @ 2022-03-16 21:35  zzzzzz2  阅读(48)  评论(0编辑  收藏  举报