LCA——树链剖分
这里是我学习LCA的最后一种方法——树链剖分。
那么首先就先讲一下树链剖分是什么:
补充一下一些概念,下面会用:
重儿子:一个节点的子节点中作为根节点的子树中节点最多的子节点。
轻儿子:一个节点的子节点中除重儿子的子节点。
链:起点为轻儿子或根节点,其余为重儿子的链。
树链剖分:顾名思义,将树剖成一条一条的链,要将树剖成链需要两个dfs。
1.搜索求出每个节点的重儿子和父节点。
2.根据节点的重儿子搜索出每一条链,记录节点的所在链和编号(链的起点越靠近根节点的编号越小,同一条链上越靠近起点的编号越小)。
下面求LCA:
若两个节点不在一条链上,将编号更大的那个更新(更新成它所在链的起点的父节点),直到他们在同一个链上时,编号更小的就是他们的最近公共祖先。
代码如下:
#includeusing namespace std; int read(){ int x=0,f=1; char a=getchar(); while(a<'0'||a>'9'){ if(a=='-')f=-1; a=getchar(); } while(a>='0'&&a<='9'){ x*=10; x+=a-'0'; a=getchar(); } return x*f; } int n,m,s; int head[500010],tot; int vis[500010],f[500010],sum[500010],zs[500010]; int dui[500010],id[500010],cnt; struct node{ int to; int nxt; }edge[1000010]; void build(int u,int v){ edge[++tot].to=v; edge[tot].nxt=head[u]; head[u]=tot; } void dfs1(int u){ sum[u]=1; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(vis[v]==0){ vis[v]=1; f[v]=u; dfs1(v); sum[u]+=sum[v]; if(sum[v]>sum[zs[u]]){ zs[u]=v; } } } } void dfs2(int u,int t){ dui[u]=t; id[u]=++cnt; if(!zs[u])return ; dfs2(zs[u],t); for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(v!=f[u]&&v!=zs[u]){ dfs2(v,v); } } } int main(){ n=read();m=read();s=read(); for(int i=1;i ){ int u,v; u=read();v=read(); build(u,v); build(v,u); } vis[s]=1; f[s]=s; dfs1(s); dfs2(s,s); for(int i=1;i<=m;i++){ int u,v; u=read();v=read(); while(dui[u]!=dui[v]){ if(id[u]>id[v]){ u=dui[u]; u=f[u]; } else{ v=dui[v]; v=f[v]; } } if(id[u]<id[v]){ printf("%d\n",u); } else{ printf("%d\n",v); } } return 0; }