洛谷 P3379 【模板】最近公共祖先(LCA)(离线)

题目链接

题意:给你一颗有n个点的树以及其根节点s,有m个询问,每次询问a,b的最近公共祖先。

思路:LCA模板题。

LCA(最近公共祖先)(离线):离线存下所有询问,然后我们可以选择dfs遍历一遍整棵树,在遍历的过程中我们设一个数组vis,还未找到的节点vis=0,找到的节点vis=1,当其所有孩子均遍历过后,vis=2。

那么据此,当我们访问到b的时候,我们可以把vis[a]分为三种情况:

  1. vis[a]=1,则b位于一颗a为根的子树上,所以LCA(a,b)=a。
  2. vis[a]=2,首先,设一个father数组,初始化father[i]=i,那么当把vis[a]置为2时,说明a的孩子已经遍历完,则设father[a]=与a最接近的vis=1的点(此通过并查集在询问的时候进行维护),而且当前正在遍历father[a]的孩子,显然,a,b不属于以father[a]为根的树的同一颗子树上,所以,LCA(a,b)=father[a]。
  3. vis[a]=0,则不用理会,当访问到a时,b会出现1或2情况。

优化:vis[a]=0的情况没有意义但程序运行的时候仍会出现。所以可以先dfs一遍预处理出树上节点的遍历顺序对于每个询问,只要在找到顺序靠后的点的时候进行判断即可。(这题不优化更快- -。。。)

该题会卡vector,故要用邻接链表存储。。

#include
#include
#include
#include
#include
#include
#include
#define inf 0x3f3f3f3f
#define p 1000000007
#define NUM 510000
using namespace std;
int m,n,s;
struct node
{
    int vertex;
    int next;
}askedge[NUM];
int aske[NUM]={};
struct link
{
    int to;
    int next;
}edge[2*NUM];
int e[NUM]={};
int ans[NUM];
int father[NUM],vis[NUM]={},tern[NUM];
int getfather(int x)
{
    if(father[x]==x)
        return x;
    return father[x]=getfather(father[x]);
}
int dfs(int v,int& t)
{
    int x;
    tern[v]=t;
    vis[v]=1;
    for(int i=e[v];i!=0;i=edge[i].next)
    {
        x=edge[i].to;
        ++t;
        if(vis[x]!=0)continue ;
        dfs(x,t);
    }
}
void LCA(int pre,int v)
{
    int x;
    node y;
    vis[v]=1;
    for(int i=aske[v];i!=0;i=askedge[i].next)
        ans[i]=getfather(askedge[i].vertex);
    for(int i=e[v];i!=0;i=edge[i].next)
    {
        x=edge[i].to;
        if(vis[x]!=0)continue ;
        LCA(v,x);
    }
    vis[v]=2;
    father[v]=getfather(pre);
}
int main()
{
    int x,y;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i


你可能感兴趣的:(LCA)