倍增LCA
Posted royal-8
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了倍增LCA相关的知识,希望对你有一定的参考价值。
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。 输入输出格式 输入格式: 第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。 接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。 接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果。 输入输出样例 输入样例#1: 复制 5 5 4 3 1 2 4 5 1 1 4 2 4 3 2 3 5 1 2 4 5 输出样例#1: 复制 4 4 1 4 4 说明 时空限制:1000ms,128M 数据规模: 对于30%的数据:N<=10,M<=10 对于70%的数据:N<=10000,M<=10000 对于100%的数据:N<=500000,M<=500000 样例说明: 该树结构如下: 第一次询问:2、4的最近公共祖先,故为4。 第二次询问:3、2的最近公共祖先,故为4。 第三次询问:3、5的最近公共祖先,故为1。 第四次询问:1、2的最近公共祖先,故为4。 第五次询问:4、5的最近公共祖先,故为4。 故输出依次为4、4、1、4、4。
为什么要用倍增来求呢?
因为快啊,不用一个一个跳,直接以2的幂次方跳。
那我们讲解一下大致过程:
首先我们需要每个点的深度和它往上2的幂次方的节点是哪个;
这个可以用dfs和dp搞出来;
dp【i】【j】表示第i个节点往上跳2的j次方后的节点是哪个;
那么有转移dp【i】【j】=dp【dp【i】【j-1】】【j-1】;
然后我们求lca
首先我们要使第一个点是深度大的那个,因为这样好写
其次我们枚举深度(2^k),当2^k<=这两个点之间的深度差的最大值时;
我们让x向上跳2^k;
这个地方有一个特判,在代码里有,懒得再打一遍了;
然后倒序继续枚举深度,如果两个点都跳2的k次方后为同一个点,那就不用跳了,因为是倒叙枚举,后面肯定要比前面优,如果不是同一个点,就跳;
最后跳完肯定是两个点都在lca的下面一个点;
那么只要求这个点的父亲返回就是lca;
下面 ↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; int f[500050][50],ans[5000000],head[5000000],deep[5000000],cnt,root,n,m,tt,flag,yqy;//f[i][j]表示第i个节点向上跳2的j次方的节点 struct node { int to,next; }edge[1000500]; void add(int x,int y) { cnt++; edge[cnt].to=y; edge[cnt].next=head[x]; head[x]=cnt; } void dfs(int x,int father){ deep[x]=deep[father]+1; f[x][0]=father;//向上一个节点就是该点的父亲; for(int i=1;(1<<i)<=deep[x];i++) f[x][i]=f[f[x][i-1]][i-1];//x向上跳2^i可以由x先向上跳2^(i-1)再跳2^(i-1)转移过来;不是2的i-1次方的i-1次方; for(int i=head[x];i;i=edge[i].next){ int y=edge[i].to; if(y!=father)dfs(y,x); } } int LCA(int x,int y) { if(deep[x]<deep[y]) { int temp=x; x=y; y=temp; }//保证x为深度大的; for(int i=20;i>=0;i--) if(deep[x]-deep[y]>=(1<<i))//如果两个节点的深度差比2的i次方大; x=f[x][i];//x就往上跳2的i次方; if(x==y) //如果跳完正好在同一个节点,那么就返回这个节点; return x; for(int k=20;k>=0;k--) if(f[x][k]!=f[y][k])//如果两个点都跳2的k次方后为同一个点,那就不用跳了,因为是倒叙枚举,后面肯定要比前面优,如果不是同一个点,就跳; x=f[x][k],y=f[y][k]; return f[y][0];//跳完之后肯定保留到两点只跳一步就相同,所以返回父亲就行了; } int main() { // ios::sync_with_stdio(0); int x,y,a,b; scanf("%d%d%d",&n,&m,&root); for(int i=1;i<=n-1;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(root,0); for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); printf("%d\n",LCA(a,b)); } return 0; }
可以转载,请注明来源
以上是关于倍增LCA的主要内容,如果未能解决你的问题,请参考以下文章