[笔记]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
原创粉丝点击