LCA算法(一)

LCA算法目前我只会离线算法(即tarjan算法),这种算法的缺点它必须读完所有询问然后才能进行输出,而且比较扯淡的是它的输出顺序不是因你的询问顺序而定的,而是由它自己的回溯顺序确定的,所以离线算法有很大的局限性,而且……我光一个离线算法就学了快一天才学会……
感觉自己蠢得像头猪一样……
LCA的tarjan算法是这样来进行的:
首先存整棵树,然后存你需要的询问,然后呢进行深搜,将每个被搜索过的点都进行标记,当搜索到一个点时,找到含有这个点的询问,如果发现没有关于这个点的询问那么可以继续算法,如果发现有关于这个点的询问,(设这个点为u,另外一个点是v),现在vis[u]一定是等于1的,而如果vis[v]也等于1,那么v这个点一定是在u之前被访问过了,也就是说,v这个点要么本身就是u的祖先,要么v这个点就是u的兄弟,要么……总之会发现:u和v现在的最近公共祖先一定是find(v)。(也就是v目前的老祖宗)
关于它,有一个不太严谨的证明:

LCA算法(一)_第1张图片
LCA算法(一)_第2张图片
LCA算法(一)_第3张图片
LCA算法要由深搜+并查集实现,模版如下:

#include <iostream>
#include <ctime>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iomanip>
#include <stack>
using namespace std;
int cnt=0;
int head[1005];
int father[1005];
bool vis[1005]; 
int q[1005][2];
int n,m;
struct star{
    int next,to,w;
}edge[1005];
void save(int u,int v){
    edge[cnt].next=head[u];
    edge[cnt].to=v;
    head[u]=cnt++;
}
int find(int x){
    int root=x;
    while(root!=father[root])root=father[root]; 
    int cn;
    while(x!=root){
        cn=father[x];
        father[x]=root;     
        x=cn;
    }
    return x;
}
void uni(int x,int y){
//  printf("%d的祖上变成了%d的祖上\n",x,y);
    father[find(x)]=father[find(y)];//x的祖宗变成了y的祖宗。
}
void LCA(int root){
    vis[root]=1;
    for(int i=1;i<=m;i++){
        if((vis[q[i][1]]&&q[i][0]==root)||(vis[q[i][0]]&&q[i][1]==root)){
            if(q[i][1]==root){
                printf("%d%d的最近公共祖先为:%d\n",root,q[i][0],find(q[i][0]));
            }
            else {
                printf("%d%d的最近公共祖先为:%d\n",root,q[i][1],find(q[i][1]));
            }
        }
    }
    for(int i=head[root];~i;i=edge[i].next){
        if(!vis[edge[i].to]){
            LCA(edge[i].to);
            uni(edge[i].to,root);
        }
    }
    return;
} 
int main (){
    cin>>n;//n个点 
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;i++){
        int u,v,w;
        scanf("%d%d",&u,&v);
        save(u,v);save(v,u);
    }
    printf("请输入询问个数:\n");
    for(int i=1;i<=n;i++)father[i]=i;
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int u,v;
        printf("请输入第%d个询问:\n",i); 
        scanf("%d%d",&u,&v);
        q[i][0]=u;q[i][1]=v;
    }
    LCA(1);
    return 0;
}

你可能感兴趣的:(算法)