【模板】lca的几种求法


1,倍增
vector<int>p[maxn];
int dep[maxn],f[maxn][20];//f[i][0]保存i的父亲
inline void dfs(int u,int fa,int d)
{
    dep[u]=d;f[u][0]=fa;
    for(int i=0;i<p[u].size();i++)
    {
        int v=p[u][i];
        if(v==fa)continue;
        dfs(v,u,d+1);
    }
}
inline void init(int n)
{
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            if(f[i][j-1])f[i][j]=f[f[i][j-1]][j-1];
    //i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
}
inline int LCA(int u,int v)
{
    int i=0,j;
    if(dep[u]<dep[v])swap(u,v);
    for(i=1;(1<<i)<=dep[u];i++);i--;//缩小j的范围 不缩小则 j从20左右开始递减
    //使a,b两点的深度相同
    for(j=i;j>=0;j--)
        if(dep[u]-(1<<j)>=dep[v])u=f[u][j];
    if(u==v)return u;
    for(j=i;j>=0;j--)
    {
        if(f[u][j]!=-1&&f[u][j]!=f[v][j])
        {
            u=f[u][j];
            v=f[v][j];
        }
    }
    return f[u][0];
}
2,树链剖分
vector<int>p[maxn];
int siz[maxn],dep[maxn],fad[maxn],son[maxn],top[maxn],cnt;
inline void dfs1(int u,int fa,int d)
{
    dep[u]=d;fad[u]=fa;siz[u]=1;
    for(int i=0;i<p[u].size();i++)
    {
        int v=p[u][i];
        if(v==fa)continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]])son[u]=v;
    }
}
inline void dfs2(int u,int t)
{
    top[u]=t;
    if(!son[u])return;//当前节点不在重链上
    dfs2(son[u],t);
    for(int i=0;i<p[u].size();i++)
    {
        int v=p[u][i];
        if(v!=son[u]&&v!=fad[u])dfs2(v,v);//非重节点的top是自己
    }
}
inline int LCA(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        u=fad[top[u]];
    }
    if(dep[u]>dep[v])return v;
    return u;
}
3,st表 (最快
vector<int>p[maxn];
int depth[maxn<<1],id[maxn],rid[maxn<<1],cnt,st[maxn<<1][25];
inline void dfs(int u,int fa,int d)
{
    id[u]=++cnt;rid[cnt]=u;depth[cnt]=d;
    for(int i=0;i<p[u].size();i++)
    {
        if(p[u][i]==fa)continue;
        dfs(p[u][i],u,d+1);
        rid[++cnt]=u;depth[cnt]=d;
    }
}
int lg[maxn<<1];
inline void init()
{
    lg[0]=-1;
    for(int i=1;i<=cnt;i++)lg[i]=lg[i>>1]+1;
    for(int i=1;i<=cnt;i++)st[i][0]=i;
    for(int j=1;(1<<j)<=cnt;j++)
        for(int i=1;i+(1<<j)-1<=cnt;i++)
            st[i][j]=depth[st[i][j-1]]<depth[st[i+(1<<j-1)][j-1]]?
                    st[i][j-1]:
                    st[i+(1<<j-1)][j-1];
}
inline int LCA(int u,int v)
{
    if(id[u]>id[v])swap(u,v);
    int s=id[u],t=id[v],len=lg[t-s+1];
    return depth[st[s][len]]<depth[st[t-(1<<len)+1][len]]?rid[st[s][len]]:rid[st[t-(1<<len)+1][len]];
}



原文地址:https://www.cnblogs.com/kkkek/p/12297320.html

时间: 2024-11-11 20:05:59

【模板】lca的几种求法的相关文章

【总结】LCA的4种求法

前言 LCA的求法有多重多样,总结下来是下面这4种.希望大家可以加油! 暴力求LCA 我们考虑dfs求出每一个点的父亲(在当前根下),然后直接先暴力跳到同一个深度,再同时跳 void dfs(int u,int f){ fa[u]=f;dep[u]=dep[f]+1; for(re int i=front[u];i;i=e[i].nxt){ int v=e[i].to; if(v==f)continue; dfs(v,u); } } int lca(int u,int v){ if(dep[u]

LCA的两种求法

HDU 2586 题意:一棵树,多次询问任意两点的路径长度. LCA:最近公共祖先Least Common Ancestors.两个节点向根爬,第一个碰在一起的结点. 求出x, y的最近公共祖先lca后,假设dist[x]为x到根的距离,那么x->y的距离为dist[x]+dist[y]-2*dist[lca] 求最近公共祖先解法常见的有两种 1, tarjan+并查集 2,树上倍增 首先是树上倍增. 1 #include <cstdio> 2 #include <cstring&

LCS的几种求法

\(LCS:\) 对于两个长度均为 \(N\) 的数列 \(A\) 和 \(B\) ,存在一个数列 \(C\) 使得 \(C\) 既是 \(A\) 的子序列有事 \(B\) 的子序列,现在需要求这个数列的最长的长度,这就是最长公共子序列. \(solution\quad 1:\) 这道题是世界上最经典的DP题之一,我们可以知道我们做需要求的子序列中的任意一个元素在 \(A\) 和 \(B\) 中都存在,所以我们可以设出状态即 \(F[i][j]\) 表示我们已经匹配了 \(A\) 的前 \(i\

逆序数的几种求法

逆序数的几种求法 白话经典算法系列之九 从归并排序到数列的逆序数对(微软笔试题)

项目案例模板之jdbc两种连接方式

项目案例模板之jdbc两种连接方式 第一种连接方式 JDBCUtils.java package jdbc; ? import org.junit.jupiter.api.Test; ? import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; ? public class JDBCUtils { public static Connection connection; pri

[模板]LCA的倍增求法解析

题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入样例#1: 5 5 4 3 1 2 4 5

模板化的七种排序算法,适用于T* vector&lt;T&gt;以及list&lt;T&gt;

最近在写一些数据结构以及算法相关的代码,比如常用排序算法以及具有启发能力的智能算法.为了能够让写下的代码下次还能够被复用,直接将代码编写成类模板成员函数的方式,之所以没有将这种方式改成更方便的函数模板纯属于偷懒,更方便于测试代码的有效性,等代码写完也懒得去改了.下面开始介绍这段代码,有什么不对的地方欢迎前来指正. 一共写了七种排序,插入排序InsertSort.堆排序HeapSort.快速排序QuickSort.合并排序MergeSort,计数排序CountingSort,基数排序RadixSo

ejs模板中的四种表达式输出形式

在ejs模板中,通常会用下面四种方式在HTML中输出服务端的变量或表达式的值: 1. 直接在<%%>中写表达式或变量.这种情况通常只是用来进行表达式计算或给变量赋值,不会有任何输出,被称作无缓冲的代码. <% code %> 2. 在<%%>中通过=号输出变量或表达式的值.默认输出到页面中的内容会进行HTML转义.如<div>Hello</div>输出后会变成<div>Hello</div> <%= code %&g

C++模板源代码的三种组织方式

模板代码和非模板代码是有区别的,如果像非模板代码那样把模板的声明放在头文件.h中,把模板的定义放在源文件.cpp中,那么使用这个模板时会得到一个链接错误.这个错误的原因在于,模板的定义还没有被实例化.为了实例化一个模板,编译器必须知道哪一个定义应该被实例化以及使用什么样的模板参数来实例化. 我们可以用以下三种方式来组织模板代码: 1. 包含模型(Inclusion Modal) a. 把模板的定义包含进声明模板的头文件中,如果模板声明在头文件tmpl.h中,定义在tmpl.cpp中,那可以将#i