全网最详系列之-倍增求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的最近公共祖先. 定 ...
随机推荐
- openCV_java 图像二值化
较为常用的图像二值化方法有:1)全局固定阈值:2)局部自适应阈值:3)OTSU等. 局部自适应阈值则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值.这样做的好处在于每个像素位置处的二值化 ...
- SQL获取本周销售总数
select sum("NUMBER") as WEEK_NUMBER, COMPANY_CODE, PROJECT_CODE from D_VISIT WHERE "D ...
- Halcon学习标定助手
本文采用halcon标定助手进行标定. 第一步:打开标定助手. 第二步:对描述文件进行修改 具体:打开算子窗口,输入gen_caltab,进行描述文件修改. 参数XNum和YNum为7行*7列的圆,M ...
- RS开发中的一些小技巧[不定期更新]
从9月份一直忙到了现在,项目整体的改版工作也完成了十有八九了,有些事情只有你自己真正的做了,你才能明白:哦,原来还可以这个样子,这样做真的好了很多呢,接下来我就分享一些最近遇到的RS开发的一些小技巧, ...
- 太阳升起并下落的小动画-SWIFT
一个小小的动画,太阳公公上山又下山.先上效果图. 用 lipecap 录的gif效果有点卡顿.好吧,说下如何实现的. 首先在一个大圆内先计算出内切九边形各个顶点的位置,接着连接相应的顶点变成一个九角星 ...
- 后台增加一个左侧列表菜单menu菜单的方法
Ecshop 后台增加一个左侧列表菜单menu菜单需要修改三个文件:/admin/includes/inc_menu.php/admin/includes/inc_priv.php/languages ...
- MVC ASP.net流程 源代码分析
AppDomainFactory.cs 1. public Object Create(String appId, String appPath) public Object Create(Strin ...
- C primer plus 练习题 第二章
6. #include <stdio.h> void echo(); int main() { /* echo(); echo(); echo(); printf("\n&quo ...
- 自动化测试管理平台ATMS(V2.0.1_8.12)下载
自动化测试管理平台ATMS(V2.0.1_8.12)下载: http://automationqa.com/forum.php?mod=viewthread&tid=2701&from ...
- Ubuntu进不入系统,一直停留在ubuntu图标画面(转)
Ubuntu进不入系统,一直停留在ubuntu图标画面(转) 在VMware中对Ubuntu进行“关闭电源”后,再次进入,一直停留在ubuntu的图标画面,无法进入系统了!网上也有别的网友碰到这个问题 ...