LCA

来源:互联网 发布:阿里云邮箱iphone7 编辑:程序博客网 时间:2024/06/03 13:14

LCA

倍增法

在线算法

时间复杂度:

  • 预处理:O(N)
  • 查询:O(log2N)

这里应用到了树中很重要的倍增思想,其实很多基础的树上操作题都可以用与 LCA 相似的方法求解。

#include<cstdio>#include<cmath>using namespace std;const int MAXN=1000005;int N,M,S;int anc[MAXN][20],deep[MAXN];void swap(int & a,int & b){int t=a;a=b;b=t;}struct E{int to,next;} e[MAXN];int ecnt,G[MAXN];void addEdge(int u, int v){e[++ecnt]=(E){v,G[u]};G[u]=ecnt;}void dfs(int u,int ln){    for(int i=G[u];i;i=e[i].next)    {        int v=e[i].to;        if(v==ln) continue;        deep[v]=deep[u]+1;        anc[v][0]=u;        dfs(v,u);            }}void getReady(){    for(int i=1;(1<<i)<=N;i++)    {         for(int j=1;j<=N;j++)            anc[j][i]=anc[anc[j][i-1]][i-1];    }}int getlca(int x, int y){    int i;    if(deep[x]<deep[y]) swap(x,y);    int maxlogn=floor(log(N)/log(2));    for(i=maxlogn;i>=0;i--)    {        if(deep[x]-(1<<i)>=deep[y])            x=anc[x][i];    }    if(x==y) return x;    for(i=maxlogn;i>=0;i--)    {        if(anc[x][i]!=anc[y][i])        {            x=anc[x][i];            y=anc[y][i];        }    }    return anc[x][0];}int main(){    scanf("%d%d%d",&N,&M,&S);    for(int i=1;i<N;i++)    {        int u,v;scanf("%d%d",&u,&v);        addEdge(u,v);addEdge(v,u);    }    dfs(S,S);    getReady();    for(int i=1;i<=M;i++)    {        int a,b;        scanf("%d%d",&a,&b);        printf("%d\n",getlca(a,b));    }    return 0;}

Tarjan法

离线算法

利用并查集,注意 dfs 时先执行递归操作再加入并查集。

#include<iostream>#include<cstdio>using namespace std;const int MAXN=1000005;int N,M,S;//CFSint E{int to,next;} e[MAXN];int ecnt,G[MAXN];void addEdge(int u,int v){e[++ecnt]=(E){v,G[u]};G[u]=ecnt;}//UFSint fa[MAXN];void getFa(int x){return fa[x]==x?fa[x]:fa[x]=getFa(x);}int A{int to,num,next;} ask[MAXN];int acnt,head[MAXN];void addAsk(int x,int y,int z){ask[++acnt]=(A){y,z,head[x]};head[x]=acnt;}int ans[MAXN],vis[MAXN];void calLCA(int u){    vis[u]=true,fa[u]=x;    int i;    for(i=G[u];i;i=e[i].next)    {        int v=e[i].to;        if(!vis[v])        {            LCA(v);            fa[v]=x;        }    }    for(i=head[u];i;i=ask[i].next)    {        int v=ask[i].to,num=ask[i].num;        if(vis[v]) ask[num]=getFa(v);    }}int main(){    scanf("%d%d%d",&N,&M,&S);    int i;    for(i=1;i<N;i++)    {        int u,v;scanf("%d%d",&u,&v);        addEdge(u,v);addEdge(v,u);    }    for(i=1;i<=M;i++)    {        int x,y;scanf("%d%d",&x,&y);        addAsk(x,y,i);addAsk(y,x,i);    }    calLCA(S);    for(i=1;i<=M;i++)        printf("%d\n",ans[i]);    return 0;}

RMQ法

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>using namespace std;const int MAXN=500000,MAXM=100;int a[MAXN+1],pa=0;int na[MAXN+1];int f[MAXN+1][MAXM+1];int head[MAXN+1],ecnt;struct node{int to,next;} edge[MAXN+1];void add(int x,int y){    ecnt++;    edge[ecnt].to=y;    edge[ecnt].next=head[x];    head[x]=ecnt;}void Init(){      for (int i = 1; i <= pa; i++)        f[i][0] = a[i];    for (int k = 1; (1 << k) <= pa; k++)        for (int i = 1; i + (1 << k) - 1 <= pa; i++)            f[i][k] = min(f[i][k - 1], f[i + (1 << (k - 1))][k - 1]);}int RMQ(int l, int r){      int k = 0;    while( (1 << (k + 1)) <= r - l + 1 ) k++;    return min( f[l][k], f[r - (1 << k) + 1][k] );}void dfs(int x,int d){    for(int tmp=head[x];tmp;tmp=edge[tmp].next)    {        a[++pa]=d;na[pa]=x;        dfs(edge[tmp].to,d+1);    }     a[++pa]=d;na[pa]=x;}int main(){    int u,v,N,M,S,x,y,xp,yp;    cin>>N>>M>>S;    for(int i=1;i<=N-1;i++)        cin>>u>>v,add(v,u);    dfs(S,1);    Init();    for(int i=1;i<=M;i++)    {           cin>>x>>y;        for(int i=1;i<=pa;i++) if(na[i]==x){xp=i;break;}        for(int i=1;i<=pa;i++) if(na[i]==y){yp=i;break;}        if(xp>yp) swap(xp,yp);        cout<<na[RMQ(xp,yp)]<<endl;    }    return 0;}
0 0
原创粉丝点击