O(1) LCA

基本思想

按欧拉序将子树对应映射到序列上,求lca(x,y),pos[x]--pos[y]中深度最小的点就是答案。

具体实现

先dfs一遍,处理出深度并完成映射。

然后用st表求出区间深度最小的点。

最后回答模仿st表即可。

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 500005
using namespace std;

int n,m,s;

struct node{
    int y,nxt;
}e[maxn*2];

int x,y;
int tot=0,head[maxn];
int dep[maxn],pos[maxn*2];
int a[maxn*2];
int dfn=0;
int f[maxn*2][22];

inline void ad(int x,int y)
{
    ++tot;
    e[tot].y=y;e[tot].nxt=head[x];head[x]=tot;
}
inline void dfs(int u,int fa)
{
    a[++dfn]=u;
    pos[u]=dfn;
    dep[u]=dep[fa]+1;
    for(register int i=head[u];i;i=e[i].nxt)
    {
        if(e[i].y!=fa)
        {
            dfs(e[i].y,u);
            a[++dfn]=u;
        }
    }
}
inline void st()
{
    memset(f,0x3f,sizeof(f));
    for(register int i=1;i<=dfn;i++)
    {
        f[i][0]=a[i];
    }
    for(register int j=1;j<=20;j++)
    {
        for(register int i=1;i+(1<<j)<=dfn;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];
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(register int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        ad(x,y);
        ad(y,x);
    }
    memset(dep,0x3f,sizeof(dep));
    dep[s]=0;
    dfs(s,0);
    st();
    int ans=0;
    for(register int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if(pos[x]>pos[y])swap(x,y);
        int l1=pos[x],l2=pos[y];
        int len=log2(l2-l1+1);
        if(dep[f[l1][len]]<dep[f[l2-(1<<len)+1][len]])ans=f[l1][len];
        else ans=f[l2-(1<<len)+1][len];
        printf("%d\n",ans);
    }
}

 

RMQ(Range Minimum Query)O(1) 方法可以用于求解最近公共祖先(LCA)问题。该方法的核心思路是将 LCA 问题转化为 RMQ 问题,通过预处理和查询操作高效地找出两个节点的最近公共祖先。 ### 转化思路 在一棵树上进行深度优先搜索(DFS),记录每个节点的首次访问时间戳,同时记录 DFS 过程中经过的节点序列(欧拉序列)以及每个节点对应的深度。对于树上的任意两个节点 u 和 v,它们的最近公共祖先 LCA(u, v) 就是欧拉序列中从 u 首次出现位置到 v 首次出现位置之间深度最小的节点。 ### 具体步骤 1. **预处理** - **DFS 遍历**:对树进行深度优先搜索,记录欧拉序列 E、每个节点首次出现的位置 first 和节点的深度数组 depth。 - **构建 RMQ 数据结构**:使用 ST 表(Sparse Table)对深度数组 depth 进行预处理,以便在 O(1) 时间内查询区间最小值。 2. **查询操作** - 对于查询的两个节点 u 和 v,找到它们在欧拉序列中首次出现的位置 first[u] 和 first[v]。 - 在深度数组 depth 对应的欧拉序列区间 [min(first[u], first[v]), max(first[u], first[v])] 上进行 RMQ 查询,找出该区间内深度最小的节点,即为 LCA(u, v)。 ### 代码示例 ```python from math import log2 # 深度优先搜索 def dfs(u, parent, d): global timer first[u] = timer E.append(u) depth[u] = d for v in graph[u]: if v != parent: timer += 1 dfs(v, u, d + 1) E.append(u) timer += 1 # 预处理 ST 表 def preprocess_st(): n = len(E) k = int(log2(n)) + 1 st = [[0] * k for _ in range(n)] for i in range(n): st[i][0] = E[i] for j in range(1, k): for i in range(n - (1 << j) + 1): if depth[st[i][j - 1]] < depth[st[i + (1 << (j - 1))][j - 1]]: st[i][j] = st[i][j - 1] else: st[i][j] = st[i + (1 << (j - 1))][j - 1] return st # RMQ 查询 def rmq_query(l, r, st): j = int(log2(r - l + 1)) if depth[st[l][j]] < depth[st[r - (1 << j) + 1][j]]: return st[l][j] else: return st[r - (1 << j) + 1][j] # 求解 LCA def lca(u, v, st): l = min(first[u], first[v]) r = max(first[u], first[v]) return rmq_query(l, r, st) # 示例用法 graph = { 1: [2, 3], 2: [1, 4], 3: [1], 4: [2] } n = len(graph) first = [0] * (n + 1) E = [] depth = [0] * (n + 1) timer = 0 # DFS 遍历 dfs(1, -1, 0) # 预处理 ST 表 st = preprocess_st() # 查询 LCA u, v = 2, 4 result = lca(u, v, st) print(f"The LCA of {u} and {v} is {result}") ``` ### 复杂度分析 - **时间复杂度**:预处理阶段的时间复杂度为 O(n log n),其中 n 是树中节点的数量。每次查询的时间复杂度为 O(1)。 - **空间复杂度**:主要用于存储欧拉序列和 ST 表,空间复杂度为 O(n log n)。 RMQ O(1) 方法通过将 LCA 问题转化为 RMQ 问题,利用 ST 表实现了高效的查询操作,适用于需要频繁查询 LCA 的场景。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值