[笔记]LCA最近公共祖先---倍增在线算法
来源:互联网 发布:淘宝专卖店都是正品吗 编辑:程序博客网 时间:2024/06/06 03:23
一.定义:(出自百度百科)
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
二.在线算法(同上):
以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。
即在可以在原树改动的情况下随时查询
三.基本思路
位运算:1< < x 等价与 1*(2^x)[至于运算范围,忘dalao指点]
1.预处理(倍增思想)
令fa[x][y]为x的2^y的祖先
初始化:fa[x][0]=father[x]
转移方程:
fa[x][y]=fa[ fa[x][y-1] ][ y-1 ]
{与RMQ类似: 2^j=2^(j-1) +2^(j-1) }
inline void get_lca(){ int k=get_k(n);// log2(n) for(int j=1;j<=k;++j) for(int i=1;i<=n;++i) if(fa[i][j-1])//终点 不可省略 fa[i][j]=fa[fa[i][j-1]][j-1];}
2.计算log2(x) {可无视}
ps:1.别用cmath函数,不是一般的慢
2.其实可以直接先用计算机试出来max_k( log2(n) ),然后一直用它
inline int get_k(int x){ int k; for(k=1;(1<<k)<=x;++k); return k-1; //记得-1}
也可打表或直接预处理出1–n的log值
for(int i=1;i<=n;++i) lg[i]=lg[i-1]+(1<<(lg[i-1]+1)==i);
3.查询
inline int query(int x,int y){ if(deep[x]<deep[y]) swap(x,y); //默认右边深度大 int k=get_k(deep[x]);//其实也可以直接用max_k int t=deep[x]-deep[y]; for(int i=0;(1<<i)<=t;++i)//注意限定条件 if((1<<i)&t)//利用二进制 5(101)则相应的i=0与i=2时,x才向上跳 x=fa[x][i]; if(x==y) return x;//特殊情况 for(int i=k;i>=0;--i)//依次缩小跳的幅度 if(fa[x][i]!=fa[y][i] && fa[x][i])//注意fa[x][i]的存在问题 { x=fa[x][i]; y=fa[y][i]; } return fa[x][0];//注意lca(x,y)为 fa[x][0]\fa[y][0]}
PS:利用log表,可进行相应简化(跳高度){ 参考luogu的讲义 }
for(;deep[x]>deep[y];) x=f[x][lg[deep[x]-deep[y]]];
四.模板
#include <cstdio>#include <cstdlib>#include <cstring>#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);#define close fclose(stdin); fclose(stdout); using namespace std;struct Edge{ int to; int next;};int n,m;int cnt;int deep[500005];int head[500005];int fa[500005][20]; //max: log2(n)+1 一条链(极端)Edge edge[1000005];inline int read(){ int k=1; int sum=0; char c=getchar(); for(;'0'>c || c>'9' ;c=getchar()) if(c=='-') k=-1; for(;'0'<=c && c<='9';c=getchar()) sum=sum*10+c-'0'; return sum*k;}inline void write(int x){ if(x<0) { putchar('-'); x*=-1; } if(x>9) write(x/10); putchar(x%10+'0');}inline void add(int x,int y){ ++cnt; edge[cnt].to=y; edge[cnt].next=head[x]; head[x]=cnt;}inline void dfs(int pre,int p){ deep[p]=deep[pre]+1; fa[p][0]=pre; for(int i=head[p];i;i=edge[i].next) if(edge[i].to!=pre && !deep[edge[i].to]) dfs(p,edge[i].to);}inline void swap(int &x,int &y){ int tmp=x; x=y; y=tmp;}inline int get_k(int x){ int k; for(k=1;(1<<k)<=x;++k); return k-1;}inline void get_lca(){ int k=get_k(n); for(int j=1;j<=k;++j) for(int i=1;i<=n;++i) if(fa[i][j-1]) fa[i][j]=fa[fa[i][j-1]][j-1];}inline int query(int x,int y){ if(deep[x]<deep[y]) swap(x,y); int k=get_k(deep[x]); int t=deep[x]-deep[y]; for(int i=0;(1<<i)<=t;++i) if((1<<i)&t) x=fa[x][i]; if(x==y) return x; for(int i=k;i>=0;--i) if(fa[x][i]!=fa[y][i] && fa[x][i]) { x=fa[x][i]; y=fa[y][i]; } return fa[x][0];}int main(){ open("3367"); n=read(); m=read(); int x1=read(); for(int i=1;i<n;++i) { int x=read(),y=read(); add(x,y); add(y,x); } dfs(0,x1); get_lca(); for(int i=1;i<=m;++i) { int x=read(),y=read(); write(query(x,y)); putchar('\n'); } close; return 0;}
阅读全文
0 0
- [笔记]LCA最近公共祖先---倍增在线算法
- 最近公共祖先 LCA 倍增算法
- 最近公共祖先LCA倍增算法
- LCA(最近公共祖先)倍增算法
- 最近公共祖先 LCA 倍增算法
- [笔记]LCA 最近公共祖先---tarjan离线算法
- LCA 在线倍增法 求最近公共祖先
- 最近公共祖先(LCA)算法实现过程 【Tarjan离线+倍增在线+RMQ】
- 最近公共祖先(LCA)及其倍增算法实现
- c++最近公共祖先LCA(倍增算法和tarjan)
- 倍增LCA(最近公共祖先)算法详解
- 树上两点最近公共祖先LCA的倍增算法 poj1986
- hdu 2586 How far away ? 最近公共祖先lca 在线算法(倍增法)/离线算法(Tarjan算法)
- 最近公共祖先(LCA)---倍增法
- 最近公共祖先(LCA):倍增
- 最近公共祖先 LCA 倍增+Tarjan实现
- poj 1986 最近公共祖先 (lca 倍增)
- 倍增法求最近公共祖先 lca
- 运动规划 (Motion Planning): MoveIt! 与 OMPL
- 程序员的一颗浮躁的心
- abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?
- Hystrix配置简单说明(官方文档简译)
- 使用ajaxFileUpload.js 上传了文件之后获取不到文件,但是回调了succcess函数。
- [笔记]LCA最近公共祖先---倍增在线算法
- 纯css的 表头固定的滚动表格
- Android Gradle配置Debug和release参数的方法
- 在CentOS6上安装 git1.9.1
- 极光推送自定义消息提示铃声
- 线程死锁
- Linux服务器时间校正
- 算法(一)
- 关于Destroy与DestroyImmediate