D11【模板】树链剖分 最近公共祖先(LCA)

D11【模板】树链剖分 最近公共祖先(LCA)_哔哩哔哩_bilibili

 

树链剖分 - OI Wiki

重儿子 表示其子结点中子树最大的子结点.

轻儿子 表示剩余的所有子结点.

连向重儿子的边为 重边

若干条首尾衔接的重边构成 重链.每条重链的头一定是 子结点.那么整棵树就被剖分成若干条重链.

树上每个结点都属于且仅属于一条重链

第一个 DFS 记录每个结点的父结点(𝑓𝑎)、深度(𝑑𝑒𝑝)、子树大小(𝑠𝑖𝑧)、重儿子(𝑠𝑜𝑛).

第二个 DFS 记录所在链的链顶(𝑡𝑜𝑝).

LCA 为两个游标跳转到同一条重链上时,深度较小的那个游标所指向的点.

树链剖分的预处理时间复杂度为 𝑂(𝑛) ,单次查询的时间复杂度为 𝑂(log⁡𝑛) ,并且常数较小.

 

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

// 树链剖分 O(mlogn)
#include<bits/stdc++.h>
using namespace std;

const int N=500010;
int n,m,s;
vector<int> e[N];

// 树剖求LCA
int fa[N],dep[N],siz[N],son[N],top[N];

void dfs1(int x,int f){ //搞fa,dep,siz,son
  fa[x]=f; dep[x]=dep[f]+1; siz[x]=1;
  for(int y:e[x])if(y!=f){
    dfs1(y,x);
    siz[x]+=siz[y];
    if(siz[son[x]]<siz[y]) son[x]=y;
  }
}
void dfs2(int x,int t){ //搞top
  top[x]=t;             //记录链顶
  if(son[x]) dfs2(son[x],t); //搜重儿子
  for(int y:e[x])if(y!=fa[x]&&y!=son[x]) dfs2(y,y); //搜轻儿子
}
int lca(int x,int y){
  while(top[x]!=top[y]) dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
  return dep[x]<dep[y]?x:y; //x,y跳到同条重链,浅的为LCA
}

int main(){
  scanf("%d%d%d",&n,&m,&s);
  for(int i=1,x,y; i<n; i++){
    scanf("%d%d",&x,&y);
    e[x].push_back(y);
    e[y].push_back(x);
  }
  dfs1(s,0);
  dfs2(s,s);
  for(int x,y;m--;){
    scanf("%d%d",&x,&y);
    printf("%d\n",lca(x,y));
  }
}

 

posted @ 2022-05-28 13:25  董晓  阅读(1893)  评论(0)    收藏  举报