[luogu3379]最近公共祖先(树上倍增求LCA)

题意:求最近公共祖先。

解题关键:三种方法,1、st表 2、倍增法 3、tarjan

此次使用倍增模板

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,root,cnt,u,v,head[500005],dep[500005],fa[500005][21];
struct edge{
    int nxt;
    int to;
}e[1000005];
void add_edge(int u,int v){//单向
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}
void dfs(int u){
    for(int i=1;(1<<i)<=dep[u];i++){
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    for(int i=head[u];~i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa[u][0]) continue;
        fa[v][0]=u;
        dep[v]=dep[u]+1;
        dfs(v);
    }
}
int lca(int u,int v){
    if(dep[u]<dep[v]) swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) u=fa[u][i];//转化到两节点深度相同,类似于快速幂的思想
    if(u==v) return u;
    for(int i=20;i>=0;i--){
        if(fa[u][i]!=fa[v][i]){
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    return fa[u][0];
}
int main(){
    memset(head,-1,sizeof head);
    scanf("%d%d%d",&n,&m,&root);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(root);
    while(m--){
        scanf("%d%d",&u,&v);
        printf("%d\n",lca(u,v));
    }
    return 0;
}

2、熟悉的树dp方式

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,root,cnt,u,v,head[500005],dep[500005],par[500005][21];
struct edge{
    int nxt;
    int to;
}e[1000005];
void add_edge(int u,int v){//单向
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}
void dfs(int u,int fa){
    for(int i=1;(1<<i)<=dep[u];i++){
        par[u][i]=par[par[u][i-1]][i-1];
    }
    for(int i=head[u];~i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa) continue;
        par[v][0]=u;
        dep[v]=dep[u]+1;
        dfs(v,u);
    }
}
int lca(int u,int v){
    if(dep[u]<dep[v]) swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;(1<<i)<=d;i++) if(d&(1<<i)) u=par[u][i];//转化到两节点深度相同,类似于快速幂的思想
    if(u==v) return u;
    for(int i=20;i>=0;i--){
        if(par[u][i]!=par[v][i]){
            u=par[u][i];
            v=par[v][i];
        }
    }
    return par[u][0];
}
int main(){
    memset(head,-1,sizeof head);
    scanf("%d%d%d",&n,&m,&root);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(root,-1);
    while(m--){
        scanf("%d%d",&u,&v);
        printf("%d\n",lca(u,v));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/elpsycongroo/p/10352437.html

时间: 2024-10-07 14:41:56

[luogu3379]最近公共祖先(树上倍增求LCA)的相关文章

CF 519E(树上倍增求lca)

传送门:A and B and Lecture Rooms 题意:给定一棵树,每次询问到达点u,v距离相等的点有多少个. 分析:按情况考虑: 1.abs(deep[u]-deep[v])%2==1时,必定不存在到达u,v距离相等的点. 2.如果deep[u]==deep[v]时,ans=n-num[lca(u,v)u在的儿子树]-num[lca(u,v)v在的儿子树]. 3.如果deep[u]!=deep[v]时,在u到v的路径中找出到达u,v相等的节点x,则ans=num[x]-num[u在的

树上倍增求LCA

大概思想就是,节点$i$的第$2^{j}$个父节点是他第$2^{j-1}$个父亲的第$2^{j-1}$个父亲 然后可以$O(nlogn)$时间内解决…… 没了? 1 //fa[i][j]表示i的第2^j个父节点 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 using namespace std; 7 struct edge{ 8 int v,n

【LCA】倍增求LCA

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

hdu 2586 How far away ? 倍增求LCA

倍增求LCA LCA函数返回(u,v)两点的最近公共祖先 #include <bits/stdc++.h> using namespace std; const int N = 40010*2; struct node { int v,val,next; node(){} node(int vv,int va,int nn):v(vv),val(va),next(nn){} }E[N]; int n,m; int tot,head[N],dis[N],f[N][20],dep[N]; void

HDU 4081 Qin Shi Huang&#39;s National Road System 最小生成树+倍增求LCA

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4081 Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5428    Accepted Submission(s): 1902 Problem Description

POJ 1986:Distance Queries(倍增求LCA)

http://poj.org/problem?id=1986 题意:给出一棵n个点m条边的树,还有q个询问,求树上两点的距离. 思路:这次学了一下倍增算法求LCA.模板. dp[i][j]代表第i个点的第2^j个祖先是哪个点,dp[i][0] = i的第一个祖先 = fa[i].转移方程:dp[i][j] = dp[dp[i][j-1][j-1]. 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm&g

tarjan,树剖,倍增求lca

1.tarjan求lca Tarjan(u)//marge和find为并查集合并函数和查找函数 { for each(u,v) //访问所有u子节点v { Tarjan(v); //继续往下遍历 marge(u,v); //合并v到u上 标记v被访问过; } for each(u,e) //访问所有和u有询问关系的e { 如果e被访问过; u,e的最近公共祖先为find(e); } } 2.倍增lca(在线) #include<bits/stdc++.h> using namespace st

(贪心+倍增求LCA) hdu 4912

Paths on the tree Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1097    Accepted Submission(s): 366 Problem Description bobo has a tree, whose vertices are conveniently labeled by 1,2,…,n. T

hiho一下 第十五周——最近公共祖先&#183;二(Trajan,离线LCA)

题目连接 http://hihocoder.com/problemset/problem/1067 题目大意 就是一棵树求任意两个节点的最近公共祖先. 算法描述 在题目的提示里面有比较详细的解释.这里就不多说了.这种算法的时间复杂度是O(n+q). 在算法的实现上也有一些技巧,在参考了一些代码后写了一个比较精简的Trajan_LAC算法. #include <bits/stdc++.h> using namespace std; typedef long long LL; const int