最近公共祖先 LCA 倍增+Tarjan实现
来源:互联网 发布:软件需求变更流程图 编辑:程序博客网 时间:2024/05/17 08:48
最近公共祖先 :
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
通常在OI中最近公共祖先的解决办法分为在线做法和离线做法,离线做法也就是Tarjan算法,而在线做法则是倍增做法。
=========================================
Tarjan做法:利用并查集优越的时空复杂度,我们可以实现LCA问题的O(n+Q)算法,这里Q表示询问的次数。Tarjan算法基于深度优先搜索的框架,对于新搜索到 的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询 问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所 有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于 进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v 所在集合的祖先。
#include<iostream>#include<cstdlib>#include<cstdio>#include<cctype>long long read() { long long num = 0, f = 1; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); } while(isdigit(ch)) num = num * 10 + ch - '0', ch = getchar(); return num * f;}const int maxn = 500010;using namespace std;int father[MAXN];int head[MAXN];int qhead[MAXN];int que[MAXN];struct Edge { int next,to;} edge[1000010];struct qEdge { int next,to,ans; qEdge() { ans=0; }} q[1000010];int cnt;void add(int x,int y) { cnt++; edge[cnt].to = y; edge[cnt].next = head[x]; head[x] = cnt;}void qadd(int x,int y,int k) { q[k].to = y; q[k].next = qhead[x]; qhead[x] = k;}int find(int x) { if(father[x] != x) father[x] = find(father[x]); return father[x];}void unionn(int x,int y) { x = find(x); y = find(y); father[y] = x;}void tarjan(int x) { que[x] = 1; for(int i = head[x]; i; i = edge[i].next) { int to = edge[i].to; if(!que[to]) { tarjan(to); unionn(x, to); } } for(int i = qhead[x]; i; i = q[i].next) { int to = q[i].to; if(que[to] == 2) { q[i].ans = find(to); if(i%2) q[i+1].ans = q[i].ans; else q[i-1].ans = q[i].ans; } } que[x] = 2;}int main() { int n = read(), m = read(), s = read(); for(int i = 1; i < n; ++i) { int x = read(), y = read(); add(x, y); add(y, x); father[i] = i; } father[n] = n; for(int i = 1; i <= m; ++i) { int x = read(), y = read(); qadd(x, y, i * 2 - 1); qadd(y, x, i * 2); } tarjan(s); for(int i = 1; i <= n; ++i) printf("%d\n",q[i * 2].ans); return 0;}
========================================
倍增做法:每次询问O(logN)
deep[i] 表示 i节点的深度, P[i,j] 表示 i 的 2^j 倍祖先
那么就有一个递推式子 P[i,j]=P[P[i,j-1],j-1]
这样子一个O(NlogN)的预处理求出每个节点的 2^k 的祖先
然后对于每一个询问的点对(a, b)的最近公共祖先就是:
先判断是否 deep[a] > deep[b] ,如果是的话就交换一下(保证 a 的深度小于 b 方便下面的操作),然后把b 调到与a 同深度, 同深度以后再把a, b 同时往上调(dec(j)) 调到有一个最小的j 满足P[a,j] != P[b,,j] (a b 是在不断更新的), 最后再把 a, b 往上调 (a = P[a,0], b = P[b,0]) 一个一个向上调直到a = b, 这时 a or b 就是他们的最近公共祖先。
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>using namespace std;const int MAXN=1000001;int n,m,root;struct node{ int u; int v; int next;}edge[MAXN];int num=1;int head[MAXN];int deep[MAXN];int f[MAXN][20];void edge_add(int x,int y){ edge[num].u=x; edge[num].v=y; edge[num].next=head[x]; head[x]=num++;}void build_tree(int p){ for(int i=head[p];i!=-1;i=edge[i].next) { int will=edge[i].v; if(deep[will]==0) { deep[will]=deep[p]+1; f[will][0]=p; build_tree(will); } }}void initialize_step(){ for(int i=1;i<=19;i++) for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];}int LCA(int x,int y){ if(deep[x]<deep[y])swap(x,y); for(int i=19;i>=0;i--) if(deep[f[x][i]]>=deep[y]) x=f[x][i]; if(x==y)return y; for(int i=19;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];}void read(int & x){ char c=getchar();x=0; while(c<'0'||c>'9')c=getchar(); while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();}int main(){ read(n);read(m);read(root); for(int i=1;i<=n;i++)head[i]=-1; for(int i=1;i<=n-1;i++){ int x,y; read(x);read(y); edge_add(x,y); edge_add(y,x); } deep[root]=1; build_tree(root); initialize_step(); for(int i=1;i<=m;i++){ int x,y; read(x);read(y); printf("%d\n",LCA(x,y)); } return 0;}
- 最近公共祖先 LCA 倍增+Tarjan实现
- 最近公共祖先(LCA):tarjan与倍增
- c++最近公共祖先LCA(倍增算法和tarjan)
- luogu3379 最近公共祖先(LCA) tarjan 倍增
- 最近公共祖先LCA(Tarjan与DFS--ST倍增)
- 最近公共祖先(LCA)算法实现过程 【Tarjan离线+倍增在线+RMQ】
- 最近公共祖先LCA tarjan
- LCA(最近公共祖先)倍增法实现
- 最近公共祖先(LCA)及其倍增算法实现
- 最近公共祖先:LCA及其用倍增实现 +POJ1986
- 最近公共祖先 LCA 倍增算法
- 最近公共祖先(LCA)---倍增法
- 最近公共祖先(LCA):倍增
- poj 1986 最近公共祖先 (lca 倍增)
- 最近公共祖先LCA倍增算法
- 倍增法求最近公共祖先 lca
- LCA(最近公共祖先)倍增算法
- 最近公共祖先 LCA 倍增算法
- 3D模型初探(人体运动学)
- 使用cJSON创建JSON字符串
- 近期交互学习记录
- Hibernate(五)一对多映射(多对一)
- 问一下大家个3D问题:旋转和缩放以及平移中如何插值矩阵?
- 最近公共祖先 LCA 倍增+Tarjan实现
- 万维链众筹
- ERROR [com.alibaba.druid.pool.DruidDataSource]
- 关于Fast Terrain Rendering Using Geometrical MipMapp
- [Kotlin]Kotlin学习笔记(一):环境搭建及Kotlin特色语法学习笔记
- phpstrom在一个窗口打开多个项目
- 继续啊
- 悟..
- Grid Coloring