最近公共祖先LCA

倍增法

变量

  • int fa[i][j]\texttt{int fa[i][j]}:节点 ii2j2^j 级祖先。
  • int dep[i]\texttt{int dep[i]}:节点 ii 的深度,即到根节点的距离。
  • int lg[i]\texttt{int lg[i]}log2i\log_2i 向下取整的整数值。

函数

  • LCA()\texttt{LCA()}:求出 lglg 数组。
  • void dfs(int x,int f)\texttt{void dfs(int x,int f)}:计算子树 xx 中的 fafadepdep 数组,xx 的父亲为 ff
  • void init(int root)\texttt{void init(int root)}:在树根为 rootroot 时计算。
  • int ask(int u,int v)\texttt{int ask(int u,int v)}:求出 uuvv 的最近公共祖先。

代码

struct LCA{
    int fa[N][LOGN],dep[N];
    int lg[N];
    LCA(){
        lg[0]=0;
        for(int i=1;i<N;i++)
            lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    }
    void dfs(int x,int f){
        fa[x][0]=f;
        dep[x]=dep[f]+1;
        for(int i=1;i<=lg[dep[x]];i++)
            fa[x][i]=fa[fa[x][i-1]][i-1];
        for(int i=G.head[x];i;i=G.nxt[i])
            if(G.ver[i]!=f)
                dfs(G.ver[i],x);
    }
    void init(int root){
        dfs(root,0);
    }
    int ask(int u,int v){
        if(dep[u]<dep[v])
            swap(u,v);
        while(dep[u]>dep[v])
            u=fa[u][lg[dep[u]-dep[v]]-1];
        if(u==v)
            return u;
        for(int i=lg[dep[u]]-1;i>=0;i--)
            if(fa[u][i]!=fa[v][i]){
                u=fa[u][i];
                v=fa[v][i];
            }
        return fa[u][0];
    }
}tree;

Tarjan法

变量

  • int v[i]\texttt{int v[i]}:标记节点的访问状态,为 00 表示未访问过,为 11 表示访问过但未访问完其子树,为 22 表示访问过且访问完其子树。
  • int lca[i]\texttt{int lca[i]}:询问 ii 查询的 lcalca 结果。
  • int fa[i]\texttt{int fa[i]}:并查集上 ii 的父节点。
  • vector<int> que[x]\texttt{vector<int> que[x]}:和点 xx 有关的询问的另一个节点。
  • vector<int> qid[x]\texttt{vector<int> qid[x]}:和点 xx 有关的询问的编号。

函数

  • void init(int n)\texttt{void init(int n)}:初始化 nn 个节点的相关数组。
  • void add_query(int x,int y,int id)\texttt{void add\_query(int x,int y,int id)}:添加询问求 x,yx,y 的最近公共祖先。
  • int get(int x)\texttt{int get(int x)}:求并查集中 xx 所在的集合的代表元素。
  • void tarjan(int x)\texttt{void tarjan(int x)}:用 tarjan\text{tarjan} 法处理所有询问。
  • void solve(int root)\texttt{void solve(int root)}:解决以 rootroot 为树根时的答案。

代码

struct LCA{
	int v[N],lca[N];
	int fa[N];
	vector<int> que[N],qid[N];
	void init(int n){
		memset(v,0,sizeof(v));
		memset(lca,0,sizeof(lca));
		for(int i=1;i<=n;i++)
			fa[i]=i;
		for(int i=1;i<=n;i++){
			que[i].clear();
			qid[i].clear();
		}
	}
	void add_query(int x,int y,int id){
		if(x==y){
			lca[id]=x;
			return;
		}
		que[x].push_back(y);
		qid[x].push_back(id);
		que[y].push_back(x);
		qid[y].push_back(id);
	}
	int get(int x){
		if(x==fa[x])
			return x;
		return fa[x]=get(fa[x]);
	}
	void tarjan(int x){
		v[x]=1;
		for(int i=G.head[x];i;i=G.nxt[i]){
			int y=G.ver[i];
			if(v[y])
				continue;
			tarjan(y);
			fa[y]=x;
		}
		for(int i=0;i<que[x].size();i++){
			int y=que[x][i],id=qid[x][i];
			if(v[y]==2)
				lca[id]=get(y);
		}
		v[x]=2;
	}
	void solve(int root){
		tarjan(root);
	}
};
posted @ 2022-09-03 22:56  luckydrawbox  阅读(18)  评论(0)    收藏  举报  来源