LCA两种方法
来源:互联网 发布:linux创建文件夹 命令 编辑:程序博客网 时间:2024/06/06 06:54
LCA
LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。
模板题
倍增法(在线)
anc[i][j]表示第i个点的2^j的祖先的标号整个过程就是两个点往上跳到同一深度,再一起往上跳找到LCA
#include<cstdio>#include<cmath>using namespace std;int n,m,root,anc[500009][21];int edge_num,head[1000009],deep[500009];struct E{ int to,next;}edge[1000009];inline void Swap(int &a,int &b){ int t=a; a=b;b=t;}inline int Read(){ int x=0,flag=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') flag=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*flag;}void addedge(int x,int y){ edge[++edge_num].next=head[x]; edge[edge_num].to=y; head[x]=edge_num;}void DFS(int x,int fa){ int i; for(i=head[x];i;i=edge[i].next){ if(edge[i].to==fa) continue;//important deep[edge[i].to]=deep[x]+1; anc[edge[i].to][0]=x; DFS(edge[i].to,x); }}void PREANC(){ int i,j; for(j=1;(1<<j)<=n;j++){ for(i=1;i<=n;i++){ if(anc[i][j-1]){//有祖先 anc[i][j]=anc[anc[i][j-1]][j-1]; } } }}int LCA(int x,int y){ if(deep[x]<deep[y]) Swap(x,y); int i; int maxlog=floor(log(n)/log(2)); for(i=maxlog;i>=0;i--){ if(deep[x]-(1<<i)>=deep[y]) x=anc[x][i]; } if(x==y) return x; for(i=maxlog;i>=0;i--){ if(anc[x][i]&&anc[x][i]!=anc[y][i]){ x=anc[x][i];y=anc[y][i]; } } return anc[x][0];}int main(){ n=Read();m=Read();root=Read(); int i; for(i=1;i<=n-1;i++){ int x=Read(),y=Read(); addedge(x,y);addedge(y,x); } DFS(root,0); PREANC(); for(i=1;i<=m;i++){ int x=Read(),y=Read(); printf("%d\n",LCA(x,y)); } return 0;}
Tarjan(离线)
Tarjan是人名
思想是DFS找到某条路最深的点,将其加入并查集,祖先为自己,然后回溯;将上一个点也加入并查集,共同祖先为上一个点。。。不断进行此操作 再DFS过程中不断加入新的点到并查集中,同时检查有没有一组问答已经在并查集内,如果有,输出一组答案
#include<cstdio>using namespace std;int n,m,root,edge_num,ask_num;int fa[500010],head[1200000],h1[1200000],ans[500010];bool b[500010];struct E{ int next,to;}edge[1200000];struct A{ int next,to,num;}ask[1200000];void addedge(int x,int y){ edge[++edge_num].next=head[x]; edge[edge_num].to=y; head[x]=edge_num;}void addask(int x,int y,int z){ ask[++ask_num].next=h1[x]; ask[ask_num].to=y; ask[ask_num].num=z; h1[x]=ask_num;}int Find(int x){ if(fa[x]!=x) fa[x]=Find(fa[x]); return fa[x];}/*void Union(int a,int b){ a=Find(a);b=Find(b); fa[a]=b;}*/void LCA(int x){ b[x]=1; fa[x]=x; int j,u; for(j=head[x];j;j=edge[j].next){ u=edge[j].to; if(!b[u]){ LCA(u); fa[u]=x; } } int k; for(j=h1[x];j;j=ask[j].next){ u=ask[j].to; k=ask[j].num; if(b[u]){ ans[k]=Find(u); } }}void out(){ for(int o=1;o<=m;o++){ printf("%d\n",ans[o]); }}int main(){ scanf("%d%d%d",&n,&m,&root); int i,ask1,ask2,a1,b1; for(i=1;i<=n-1;i++){ scanf("%d%d",&a1,&b1); addedge(a1,b1);addedge(b1,a1); } for(i=1;i<=m;i++){ scanf("%d%d",&ask1,&ask2); addask(ask1,ask2,i); addask(ask2,ask1,i); } LCA(root); out(); return 0;}
相比之下,Tarjan比LCA要快几乎一倍,但倍增可以在两点到LCA的路上很容易地搞事情,比如NOIP2013 DAY1 T3
0 0
- LCA两种方法
- LCA 的两种算法
- LCA问题的两种解法
- POJ-1330 LCA的两种写法
- POJ1986-LCA问题的在线离线两种算法
- 求LCA的几种方法
- 求LCA的几种方法
- LCA离线方法
- 两种keyboardinput方法
- 模式识别两种方法
- 两种方法求丑数
- 缩略图两种方法
- 两种封装方法
- 排列组合(两种方法)
- POJ1703 两种方法
- 两种删除方法
- LCA实现的三种不同的方法
- 最近公共祖先(LCA)的三种求解方法
- getOutputStream与getWriter方法
- redis直接存取object
- python dir()
- mysql index-condition-pushdown
- 如何从宿主机拖动复制文件到虚拟机VM中的解决方法
- LCA两种方法
- 金科中学要如何才能变成金科一中
- Windows10+VS2013环境下Dlib库的编译与使用
- Frames of Reference 参考系
- 谓词下推
- 并查集算法和路径压缩
- css less使用以及注意点
- 【Spark你妈喊你回家吃饭-01】 Spark是什么鬼?
- Python自动化运维课程学习--Day1