全网最详系列之-倍增求LCA
1,什么是LCA
LCA。最近公共祖先。是一个在解决树上问题最强劲有力的一个工具。一般都是指。在一棵树上取两个节点a,b 。另一个节点x它满足 x是a与b的祖先而且x深度最大。这个x就是节点a,b的最近公共祖先。
2,什么是树上倍增。
树上倍增。其实就是通过二进制拆分。将规则一定情况下加速区间状态转移,的一种奇技淫巧。实质是根据已经得到的信息,将考虑的范围扩大一倍,从而加速操作的思想。 其实就是预处理出一个表。每次通过翻倍区间去查询。 >往下看<可能会更清楚。毕竟实践是检验真理的唯一标准。
3,How to 实现它
首先,来一棵树。
其次 我们有一个数组来存,一个节点第几个祖先是谁。
二维:F[i][j] 其中j代表子节点。i代表j这个节点第2^i个祖先。
所以我们要做的就是预处理。将 j节点每第2^i个祖先先存起来。之后就是倍增查询就好。而这里的i其实没有限度。但是一般情况下,20就够用了。2^20就已经够大了。也不能再大不过也没必要。
for(int i=1;i<=n;++i)f[0][i]=father[i];//这里可以在建边的时候操作。
for(int i=1;i<=20;++i)
for(int j=1;j<=n;++j)
f[i][j]=f[i-1][f[i-1][j]];//关键转移。
关注这里f[i][j]=f[i-1][f[i-1][j]];这里f[i-1][j]指的是上一个2^(i-1)的祖先的位置。而我们又要查询这个祖先节点的2^(i-1)其实就是翻了一倍(准确说不是一倍,感性上可以这么认知) 举个栗子。我们这里要找10号节点2^1的祖先的位置。而我们先找10第2^(1-1)祖先的位置 也就是 6号节点。之后再找6号节点2^(1-1)的位置也就是 2号节点。而我们这里找到的2号节点也就是 10号节点第2^1个祖先的位置。
而第一行的处理是节点父亲的处理。这个可以放到建边的时候进行,就不需要用储存并查集,浪费的很。
这里预处理好等着我们的就是查询了。
还是这张图。任意取两个点 8号节点和6号节点。我们查询这两个点的LCA。
1,判断深度。如果深度不同。得把深度大的点跳转到同一深度。(因为接下来要同时跳转所以得达到相同起点)。而这里跳每次当遇到祖先的深度比小深度的节点,
2,迷之审判。如果2个节点刚好在同一个链上而且,1个刚好是另一个祖先。那我们跳完之后还得判断一下跳完之后的节点和另一个节点是不是相同。
3,同时跳转。每次去寻找它们第2^i个祖先(从大往小找)。如果都相同说明 嗯~都相同可能是他们祖先的祖先的祖先的祖先。但是一旦出现不相同的说明。可能离最近公共祖先可能更近了。就跳到当前节点去,继续重复这一条,直到他们的父亲是相同的。那说明,此时,他们的父亲,就是初始的2个点的最近公共祖先。
int LCA(int x,int y)
{
if(dep[x]>dep[y])swap(x,y); //步骤1
for(int i=20;~i;--i) //注释1
{
if(dep[f[i][y]]>=dep[x])
{
y=f[i][y];
}
}
if(y==x)return x; //步骤2
for(int i=20;~i;--i) //步骤3
{
if(f[i][x]!=f[i][y]) //注释2
{
x=f[i][x];
y=f[i][y];
}
}
return f[0][x];
}
注释1:我们会发现。为什么这里就只有一层循环,而我们每次跳都要继续往上跳。每次上一次跳的节点2^i步都没有跳过,当2^(i-1)时跳过去。之后跳的步数也不可能再大于2^(i-1)所以这里只有一层循环。每次都在逼近答案。如果深度相同,或者小于。那么就可以跳。反正当都相同了。下面的循环做其实也都没意义了。
注释2:我们发现这里如果f[i][x]==f[i][y]的情况出现,那么就有两种事情发生。1,是他们的祖先的祖先的祖先....2,是他们的LCA。但是显然我们没法分辨这个东西。所以我们要在不相同的时候跳转,每次都在逼近答案。而我们达到他们两个节点的父亲相同了,那么就找到LCA了。只有一层循环的原因同上。所以最后返回的也是他的父亲。
这就是找LCA的倍增的方法。这个方法是我最先掌握的。。其实也不是很难。。
4,来道题强化一下
codevs 2370 小机房的树
时间限制: 1 s
空间限制: 256000 KB
题目等级 : 钻石 Diamond
题目描述 Description
小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力
输入描述 Input Description
第一行一个n,接下来n-1行每一行有三个整数u,v, c 。表示节点 u 爬到节点 v 需要花费 c 的精力。
第n+1行有一个整数m表示有m次询问。接下来m行每一行有两个整数 u ,v 表示两只虫子所在的节点
输出描述 Output Description
一共有m行,每一行一个整数,表示对于该次询问所得出的最短距离。
样例输入 Sample Input
3
1 0 1
2 0 1
3
1 0
2 0
1 2
样例输出 Sample Output
1
1
2
数据范围及提示 Data Size & Hint
1<=n<=50000, 1<=m<=75000, 0<=c<=1000
分析:这道题,有点裸。首先求出两点的LCA,之后无论在什么情况下跳转点的时候都要纪录长度。注意建树的时候方向。这个小细节要注意好。还有。不要搞基。搞gay对身体不好。。虽然1句话题解感觉很不负责任。但是。没什么要讲的了吧。。。。
#include<algorithm>
#include<cstdio>
#include<string.h>
using namespace std;
struct node{
int val,mean;
}f[21][500100];
int n,m,c;
int head[500010],cnt;
int dep[500010];
struct node_1{
int v,next,val;
}edge[100010];
void add(int x,int y,int val)
{
edge[++cnt].v=y;
edge[cnt].next=head[x];
edge[cnt].val=val;
head[x]=cnt;
edge[++cnt].v=x;
edge[cnt].next=head[y];
edge[cnt].val=val;
head[y]=cnt;
f[0][x].mean=y;
f[0][x].val=val;
return ;
}
int visit[100010];
void DFS(int x,int step)
{
visit[x]=1;
dep[x]=step;
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(visit[edge[i].v])continue;
DFS(edge[i].v,step+1);
}
return ;
}
long long int LCA(int x,int y)
{
long long int ans=0;
if(dep[x]>dep[y])swap(x,y);
for(int i=20;~i;--i)
{
if(dep[f[i][y].mean]>=dep[x])//不要忘了这的等号
{ ans+=f[i][y].val;
y=f[i][y].mean;
}
}
if(x==y)return ans;
for(int i=20;~i;--i)
{
if(f[i][x].mean!=f[i][y].mean)
{
ans=f[i][x].val+f[i][y].val+ans;
x=f[i][x].mean;
y=f[i][y].mean;
}
}
ans=ans+f[0][x].val+f[0][y].val;
return ans;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(int i=0;i<n;++i)dep[i]=1;
int x,y,z;
for(int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
add(y,x,z);
}
DFS(0,1);//对深度的处理
f[0][0].val=0;f[0][0].mean=0;
for(int i=1;i<=20;++i)
for(int j=0;j<n;++j)
{
f[i][j].val=f[i-1][j].val+f[i-1][f[i-1][j].mean].val;
f[i][j].mean=f[i-1][f[i-1][j].mean].mean;
}
int a,b,q;
scanf("%d",&q);
for(int i=1;i<=q;++i)
{
scanf("%d%d",&a,&b);
printf("%lld\n",LCA(a,b));//不开longlong见祖宗,十年OI一场空。
}
return 0;
}
嗯。就是这样
全网最详系列之-倍增求LCA的更多相关文章
- 树上倍增求LCA(最近公共祖先)
前几天做faebdc学长出的模拟题,第三题最后要倍增来优化,在学长的讲解下,尝试的学习和编了一下倍增求LCA(我能说我其他方法也大会吗?..) 倍增求LCA: father[i][j]表示节点i往上跳 ...
- [算法]树上倍增求LCA
LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...
- 【倍增】洛谷P3379 倍增求LCA
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- hdu 2586 How far away ? 倍增求LCA
倍增求LCA LCA函数返回(u,v)两点的最近公共祖先 #include <bits/stdc++.h> using namespace std; *; struct node { in ...
- 倍增求lca模板
倍增求lca模板 https://www.luogu.org/problem/show?pid=3379 #include<cstdio> #include<iostream> ...
- 【题解】洛谷P4180 [BJWC2010] 严格次小生成树(最小生成树+倍增求LCA)
洛谷P4180:https://www.luogu.org/problemnew/show/P4180 前言 这可以说是本蒟蒻打过最长的代码了 思路 先求出此图中的最小生成树 权值为tot 我们称这棵 ...
- 倍增求LCA学习笔记(洛谷 P3379 【模板】最近公共祖先(LCA))
倍增求\(LCA\) 倍增基础 从字面意思理解,倍增就是"成倍增长". 一般地,此处的增长并非线性地翻倍,而是在预处理时处理长度为\(2^n(n\in \mathbb{N}^+)\ ...
- 树链剖分与倍增求LCA
树链剖分与倍增求\(LCA\) 首先我要吐槽机房的辣基供电情况,我之前写了一上午,马上就要完成的时候突然停电,然后\(GG\)成了送链剖分 其次,我没歧视\(tarjan LCA\) 1.倍增求\(L ...
- [学习笔记] 树上倍增求LCA
倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...
- 树上倍增求LCA详解
LCA(least common ancestors)最近公共祖先 指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先. 定 ...
随机推荐
- Android系列--DOM、SAX、Pull解析XML
您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...
- java产生随机数的几种方式
java产生随机数的几种方式 一.在j2se里我们可以使用Math.random()方法来产生一个随机数,这个产生的随机数是0-1之间的一个double,我们可以把他乘以一定的数,比如说乘以100,他 ...
- jQuery之Deferred对象详解
deferred对象是jQuery对Promises接口的实现.它是非同步操作的通用接口,可以被看作是一个等待完成的任务,开发者通过一些通过的接口对其进行设置.事实上,它扮演代理人(proxy)的角色 ...
- Web 开发常备工具
工欲善其事,必先利其器.如今 Web 开发标准越来越高,Web 开发者也在不断寻找途径提升自己的技能.为使大家的开发工作更顺利进行,本文整理了 10+ 款比较优秀的 Web 开发工具,希望对你有帮助. ...
- EntityFramework 5.0 CodeFirst 教程04-查询,插入,更新,和删除数据
---------------------目录-------------------------- EntityFramework 5.0 CodeFirst 教程04-查询,插入,更新,和删除数据 ...
- iSAC测试报告
iSAC测试报告 测试码流:24k bit/s 测试环境:三星i9250 CPU 1.2G*2 ram:1G TI芯片 OMAP 4460 双核1.2GHz MOTO ME722 CPU ...
- .net微信公众号开发——群发消息
作者:王先荣 本文将介绍微信公众号开发中用于群发消息的类MassMessage,包括:(1)MassMessage类:(2)群发:(3)删除:(4)预览:(5)查询发送状态:(6)接收推送群发结 ...
- UDP"打洞"原理
1. NAT分类 根据Stun协议(RFC3489),NAT大致分为下面四类 1) Full Cone 这种NAT内部的机器A连接过外网机器C后,NAT会打开一个端口.然后外网的任何发到这个打开的端口 ...
- [AX2012 R3]关于Alerts
AX2012提供两种类型的Alert,Change-based alert和Due-date-based alert,前者用于在对新建记录.删除记录.记录的某个指定字段被改变的时候发出提醒,后者则是用 ...
- [原创]Android Studio的Instant Run(即时安装)原理分析和源码浅析
Android Studio升级到2.0之后,新增了Instant Run功能,该功能可以热替换apk中的部分代码,大幅提高测试安装的效率. 但是,由于我的项目中自定义了一些ClassLoader,当 ...