洛谷 P3379 【模板】最近公共祖先(LCA)

洛谷 P3379 【模板】最近公共祖先(LCA)_第1张图片


洛谷里面8页题解千篇一律,就没有用线段树求解的,这下不得不由本蒟蒻来生啃又臭又硬,代码又多的线段树了。

洛谷 P3379 【模板】最近公共祖先(LCA)_第2张图片

样例的欧拉序列:4 2 4 1 3 1 5 1 4

记录每个节点最早在欧拉序列中的时间,任意两个节点的LCA就是他们两个节点在欧拉序列之间,深度最小的那一个。(证明看洛谷题解)

比如2和5的LCA:4 2 4 1 3 1 5 1 4,4是深度最小的。

很显然这是RMQ问题,可以用ST表求解,但我偏不,多次询问区间求极值可以用线段树来维护。

此处维护的就是

注意建树内存大小是维护欧拉序列的大小,而不是原序列,欧拉序列是原序列的2倍大小,所以线段数要开8倍的内存。

AC代码

#include 
using namespace std;
const int N=5e5+1;

int n,m,s,oula[N<<1],d[N],ti[N],t[N<<3];//注意是8倍大小
vectore[N];
bitsetvis;

inline int read(){
    int x=0;char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<3)+(x<<1)+(c xor 48),c=getchar();
    return x;
}

int main(){
    n=read(),m=read(),s=read();
    for(int i=1,x,y;i<=n-1;++i){
        x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    
    d[s]=1,n=0;
    auto dfs=[](int x,auto dfs){
        if(vis[x])return;
        vis[x]=true;
        oula[++n]=x;//欧拉序列
        for(auto i:e[x]){
            if(!vis[i]){
                d[i]=d[x]+1;//深度
                dfs(i,dfs);
                oula[++n]=x;//欧拉序列
            }
        }
    };dfs(s,dfs);
    for(int i=1;i<=n;++i)if(!ti[oula[i]])ti[oula[i]]=i;//欧拉时间戳

    auto build=[](int l,int r,int i,auto build){
        if(l==r){
            t[i]=l;//叶子节点,欧拉时间戳
            return;
        }
        int mid=l+r>>1;
        build(l,mid,i<<1,build);
        build(mid+1,r,i<<1|1,build);
        t[i]=d[oula[t[i<<1]]]ti[b])swap(a,b);
        auto query=[](int l,int r,int i,int x,int y,auto query){
            int ans=0;
            if(l>=x and r<=y)return t[i];//返回欧拉时间戳
            int mid=l+r>>1;
            if(x<=mid){
                int idx=query(l,mid,i<<1,x,y,query);
                ans=d[oula[idx]]mid){
                int idx=query(mid+1,r,i<<1|1,x,y,query);
                ans=d[oula[idx]]

需要注意的是,开O2并不会带来任何优化,本蒟蒻代码是厌氧代码

你可能感兴趣的:(洛谷,c++,算法,数据结构,线段树,欧拉序列)