D11【模板】树链剖分 最近公共祖先(LCA)
D11【模板】树链剖分 最近公共祖先(LCA)_哔哩哔哩_bilibili
重儿子 表示其子结点中子树最大的子结点.
轻儿子 表示剩余的所有子结点.
连向重儿子的边为 重边.
若干条首尾衔接的重边构成 重链.每条重链的头一定是 轻子结点.那么整棵树就被剖分成若干条重链.
树上每个结点都属于且仅属于一条重链.

第一个 DFS 记录每个结点的父结点(𝑓𝑎
)、深度(𝑑𝑒𝑝
)、子树大小(𝑠𝑖𝑧
)、重儿子(𝑠𝑜𝑛
).
第二个 DFS 记录所在链的链顶(𝑡𝑜𝑝
).
LCA 为两个游标跳转到同一条重链上时,深度较小的那个游标所指向的点.
树链剖分的预处理时间复杂度为 𝑂(𝑛) ,单次查询的时间复杂度为 𝑂(log𝑛)
,并且常数较小.
// 树链剖分 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)); } }
浙公网安备 33010602011771号