lca的三种算法【倍增 / RMQ / Tajan】

  • lca:树上两点的最近公共祖先

倍增算法(在线)

  1. 利用dfs记录结点深度deep[i],并求出单点祖先:f[i][j]:从结点i向上出发 2j 步的祖先,可推出→
    f[i][0] = fa[i], f[i][j] = f[f[i][j - 1]][j - 1]
  2. 求两点x,y的lca,不妨令deep[x] >= deep[y],x先爬deep[x]-deep[y]步使得x和y的deep相同;若此时x == y,则x就是最近公共祖先;否则x和y同时爬一些步数后,若到达的点相同,则找到公共祖先,第一个这样的点,就是最近公共祖先。
  3. 代码:
inline void dfs(int u, int fa)
{
    deep[u] = deep[fa] + 1;
    f[u][0] = fa;
    for(int i = 1; i <= 20 && f[u][i - 1] > 0; ++ i)
        f[u][i] = f[f[u][i - 1]][i - 1];
    for(int i = head[u]; i; i = e[i].next)
    if (e[i].to != fa) dfs(e[i].to, u);
}
inline int lca(int x, int y)
{
    if (deep[x] < deep[y]) swap(x, y);
    for(int i = 20; i >= 0; -- i)
    if (deep[f[x][i]] >= deep[y]) x = f[x][i];
    if (x == y) return x;
    for(int i = 20; i >= 0; -- i)
    if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
    return f[x][0];
}
  • 时间复杂度O(n * log n + q * log n)

RMQ算法(在线)

  1. dfs并用数组a[0]按时间戳递增记录访问与回溯的结点,用a[1]记录a[0]中结点的深度,并用fir数组标记每一个点在a[0]中第一次出现的时间。
    如:【手动画图莫嫌弃QAQ
    对于上图中的树,
    a[0] = {1, 2, 4, 2, 5, 6, 5, 7, 5, 2, 1, 3, 8, 3, 1};
    a[1] = {0, 1, 2, 1, 2, 3, 2, 3, 2, 1, 0, 1, 2, 1, 0};
    fir = {1, 2, 12, 3, 5, 6, 8, 13};
  2. 经过推敲发现,对于两个点x,y的lca为a[1]中(fir[x], fir[y])的最小值所对应a[0]的点,可用RMQ快速查询。
  3. 代码略去。因与倍增同为在线,我较少使用【但貌似效率较高。推荐一版代码

Tajan算法(离线)

  1. Tarjan算法基于从根节点开始DFS时,在刚刚好访问完两个节点时,这两个节点的LCA必定没有在DFS中回溯。
  2. 当访问完x的每棵子树时,用并查集把这些子树记为与x同集。
  3. 可发现当dfs访问到v时,u以访问,lca(u, v) = find(v)。【建议手模一下】
  4. 代码:
inline int find(int x)
{
    return p[x] == x ? x : p[x] = find(p[x]);
}
inline void dfs(int u, int fa)
{
    vi[u] = true;
    for(int i = head[u]; i; i = e[i].next)
    if (e[i].to != fa)
    {
        int v = e[i].to;
        dfs(v, u);
        p[v] = u;
    }
    for(int i = qh[u]; i; i = q[i].next)
    if (vi[q[i].to]) ans[q[i].id] = find(q[i].to);
}
  • 时间复杂度:O(n + q)

答案便于记录时,个人感觉tajan好用,简短而快速。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值