LCA三种简洁模板,tarjan+并查集,倍增法,st+dfs,树剖。

先上一个洛谷的模板题,一下模板皆适用,为AC代码。

https://www.luogu.org/problemnew/show/P3379

1.tarjan+并查集  源于https://blog.csdn.net/li1615882553/article/details/79762771

#include<cstdio>
const int N = 5e5+10;
struct hehe{
    int next;
    int to;
    int lca;
};
hehe edge[N<<1];//树的链表
hehe qedge[N<<1];//需要查询LCA的两节点的链表
int n,m,p,x,y;
int num_edge,num_qedge,head[N<<1],qhead[N];
int father[N];
int visit[N];//判断是否被找过
void add_edge(int from,int to){//建立树的链表
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    head[from]=num_edge;
}
void add_qedge(int from,int to){//建立需要查询LCA的两节点的链表
    qedge[++num_qedge].next=qhead[from];
    qedge[num_qedge].to=to;
    qhead[from]=num_qedge;
}
int find(int z){//找爹函数
    if(father[z]!=z)
        father[z]=find(father[z]);
    return father[z];
}
int dfs(int x){//把整棵树的一部分看作以节点x为根节点的小树
    father[x]=x;//由于节点x被看作是根节点,所以把x的father设为它自己
    visit[x]=1;//标记为已被搜索过
    for(int k=head[x];k;k=edge[k].next)//遍历所有与x相连的节点
        if(!visit[edge[k].to]){//若未被搜索
            dfs(edge[k].to);//以该节点为根节点搞小树
            father[edge[k].to]=x;//把x的孩子节点的father重新设为x
        }
    for(int k=qhead[x];k;k=qedge[k].next)//搜索包含节点x的所有询问
        if(visit[qedge[k].to]){//如果另一节点已被搜索过
            qedge[k].lca=find(qedge[k].to);//把另一节点的祖先设为这两个节点的最近公共祖先
            if(k%2)//由于将每一组查询变为两组,所以2n-1和2n的结果是一样的
                qedge[k+1].lca=qedge[k].lca;
            else
                qedge[k-1].lca=qedge[k].lca;
        }
}
int main(){
    scanf("%d%d%d",&n,&m,&p);//输入节点数,查询数和根节点
    for(int i=1;i<n;++i){
        scanf("%d%d",&x,&y);//输入每条边
        add_edge(x,y);
        add_edge(y,x);
    }
    for(int i=1;i<=m;++i){
        scanf("%d%d",&x,&y);//输入每次查询,考虑(u,v)时若查找到u但v未被查找,所以将(u,v)(v,u)全部记录
        add_qedge(x,y);
        add_qedge(y,x);
    }
    dfs(p);//进入以p为根节点的树的深搜
    for(int i=1;i<=m;i++)
        printf("%d\n",qedge[i*2].lca);//两者结果一样,只输出一组即可
    return 0;
}

2. st+dfs思想 st表可离线求数列任意区间最大最小值。LCA基于将树编号后任意两点LCA就是他们之间点编号的最小值。

/*
st表+dfs   求LCA
先对树链进行搜索编号,
利用了两个节点间的公共
祖先就是他们之间编号的最小值的原理。
*/
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 500010;
int n, m, s, fp[MAXN], id[MAXN], pn, an, dp[MAXN*2][21];
int head[MAXN<<1], to[MAXN*2], nxt[MAXN*2], cnt;
inline void add(int u, int v) {
    to[++cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt;
}
void dfs(int u, int f) {
    int i, c;
    id[c = ++pn] = u;
    dp[fp[u] = ++an][0] = c;
    for(i = head[u]; i; i = nxt[i]) {
        if(to[i] == f) continue;
        dfs(to[i], u);
        dp[++an][0] = c;
    }
}
void Sparse_Table(){
    //st表精华
    int i, j;
    for(j = 1; (1<<j) <= an; j++)
        for(i = 1; i+(1<<j)-1 <= an; i++)
            dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}
inline int LCA(int l, int r) {
    int i, k = (int) (log(r-l+1)/log(2));
    return id[min(dp[l][k], dp[r-(1<<k)+1][k])];
}
int main() {
    int i, u, v;
    cin>>n>>m>>s;
    for(i = 1; i < n; i++) {
        scanf("%d%d",&u,&v);
        add(u, v);add(v, u);
    }
    dfs(s, -1);
    Sparse_Table();
    for(i = 1; i <= m; i++) {
        //cin>>u>>v;
        scanf("%d%d",&u,&v);
        if(fp[u]>fp[v]) swap(u, v);
        printf("%d\n", LCA(fp[u], fp[v]));
    }
    return 0;
}

3.倍增法求LCA 在线查询,适用性更广。

 

//倍增法在线查询LCA
#include<bits/stdc++.h>
using namespace std;const int N=5e5+10;
int n,m,s,x,y,f[N][18],bin[20],d[N];
int head[N<<1],to[N<<1],nxt[N<<1],tot;
void add(int u,int v){  //简单前向星建边
    to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
//  搜索初始化
void dfs(int x){//BIN存储2的几次方
    //这步为何?
    for(int i=1;bin[i]<=d[x];i++) f[x][i]=f[f[x][i-1]][i-1];
    for(int i=head[x];i;i=nxt[i]){
        if(to[i]==f[x][0]) continue;
        d[to[i]]=d[x]+1;f[to[i]][0]=x;dfs(to[i]);
    }
}
inline int lca(int x,int y){
    if(d[x]<d[y]) swap(x,y);
    int t=d[x]-d[y];
    //下面操作保证搜到同层次
    for(int i=0;bin[i]<=t;i++) if(bin[i]&t) x=f[x][i];
    for(int i=17;i>=0;i--){
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    }
    if(x==y) return x;return f[x][0];
}
int main(){
    bin[0]=1;
    for(int i=1;i<20;i++) bin[i]=bin[i-1]<<1;
    cin>>n>>m>>s;
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(s);
    while(m--){
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;
}

  OJXK 。 感谢吴大佬支持!!!

4.罗大佬发明了树剖求LCA,效率优于上述3个,代码写得较早,各位大佬可自行查阅。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值