LCA最近公共祖先 線上演算法和離線演算法 模板

life4711發表於2015-03-01

原理講解:http://dongxicheng.org/structure/lca-rmq/


線上演算法模板:

[cpp] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片
#include <cstdio>  
#include <iostream>  
#include <cstring>  
#include <algorithm>  
  
using namespace std;  
const int INF=0x3f3f3f;  
const int maxn=111111;  
const int maxm=111111;  
int n,m;  
  
struct EDGENODE{  
    int to;  
    int w;  
    int next;  
};  
struct SGRAPH{  
    int head[maxn];  
    EDGENODE edges[maxm];  
    int edge;  
    void init(){  
        memset(head,-1,sizeof(head));  
        edge=0;  
    }  
    void addedge(int u,int v,int c){  
        edges[edge].w=c,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;  
    }  
    //------------  
    int d[maxn][20];  
    //元素從1編號到n  
    void makeRmqIndex(int A[],int n){  
        for(int i=1;i<=n;i++) d[i][0]=i;  
        for(int j=1;(1<<j)<=n;j++)  
            for(int i=1;i+(1<<j)-1<=n;i++)  
                d[i][j] = A[d[i][j-1]] < A[d[i+(1<<(j-1))][j-1]]? d[i][j-1]:d[i+(1<<(j-1))][j-1];  
    }  
    int rmqIndex(int L,int R,int A[])  
    {  
        int k=0;  
        while ((1<<(k+1))<=R-L+1) k++;  
        return A[d[L][k]]<A[d[R-(1<<k)+1][k]]? d[L][k]:d[R-(1<<k)+1][k];  
    }  
    //---------------------  
    int E[maxn*2],R[maxn],D[maxn*2],mn;  
    void dfs(int u,int p,int d){  
        E[++mn]=u;  
        D[mn]=d;  
        R[u]=mn;  
        for (int i=head[u];i!=-1;i=edges[i].next){  
            int v=edges[i].to;  
            if (v==p) continue;  
            dfs(v,u,d+1);  
            E[++mn]=u;  
            D[mn]=d;  
        }  
    }  
    void LCA_init(){  
        mn=0;  
        memset(R,0,sizeof(R));  
        dfs(1,-1,1);  
        makeRmqIndex(D,mn);  
        getd(1,-1,0);  
    }  
    int LCA(int u,int v){  
        if (R[u]>=R[v]) return E[rmqIndex(R[v],R[u],D)];  
        else return E[rmqIndex(R[u],R[v],D)];  
  
    }  
    //--------------------  
    int deep[maxn];  
    void getd(int u,int p,int w){  
        deep[u]=w;  
        for (int i=head[u];i!=-1;i=edges[i].next){  
            int v=edges[i].to;  
            if (v==p) continue;  
            getd(v,u,w+edges[i].w);  
        }  
    }  
    int getDis(int u,int v){  
        int lca=LCA(u,v);  
        return deep[u]+deep[v]-deep[lca]*2;  
    }  
    int done(int x,int y,int z){  
        int ans=INF,res=0;  
        int lca1,lca2;  
  
        lca1=LCA(x,y);  
        res=deep[x]+deep[y]-deep[lca1]*2;  
        lca2=LCA(lca1,z);  
        res+=deep[lca1]+deep[z]-deep[lca2]*2;  
        ans=min(ans,res);  
  
        lca1=LCA(x,z);  
        res=deep[x]+deep[z]-deep[lca1]*2;  
        lca2=LCA(lca1,y);  
        res+=deep[lca1]+deep[y]-deep[lca2]*2;  
        ans=min(ans,res);  
  
        lca1=LCA(y,z);  
        res=deep[y]+deep[z]-deep[lca1]*2;  
        lca2=LCA(lca1,x);  
        res+=deep[lca1]+deep[x]-deep[lca2]*2;  
        ans=min(ans,res);  
  
        return ans;  
    }  
}solver;  


離線求解:

/**
LCA(離線演算法)
主函式除建邊外還應呼叫
init();
dir[1]=0;
tarjan(1);
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=40010;

struct note
{
    int u,v,w,lca,next;
}edge[maxn*2],edge1[805];

int head[maxn],ip,head1[maxn],ip1;///需要建兩次邊。1,該樹的邊2,需要查詢的兩點
int m,n;
int father[maxn],vis[maxn],dir[maxn];
///依次表示u點的祖先、標記是否訪問過,到根節點的距離

void init()
{
    memset(vis,0,sizeof(vis));
    memset(dir,0,sizeof(dir));
    memset(head,-1,sizeof(head));
    memset(head1,-1,sizeof(head1));
    ip=ip1=0;
}

void addedge(int u,int v,int w)
{
    edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}

void addedge1(int u,int v)
{
    edge1[ip1].u=u,edge1[ip1].v=v,edge1[ip1].lca=-1,edge1[ip1].next=head1[u],head1[u]=ip1++;
}

int  Find(int x)
{
    if(father[x]==x)
        return x;
    return father[x]=Find(father[x]);
}

void Union(int x,int y)
{
    x=Find(x);
    y=Find(y);
    if(x!=y)
        father[y]=x;
}

void tarjan(int u)
{
    vis[u]=1;
    father[u]=u;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        int w=edge[i].w;
        if(!vis[v])
        {
           dir[v]=dir[u]+w;
           tarjan(v);
           Union(u,v);
        }
    }
    for(int i=head1[u];i!=-1;i=edge1[i].next)
    {
        int v=edge1[i].v;
        if(vis[v])
        {
            edge1[i].lca=edge1[i^1].lca=father[Find(v)];
        }
    }
}



相關文章