LCA树上倍增求法
1.LCA
LCA就是最近公共祖先(Least common ancestor),x,y的LCA记为z=LCA(x,y),满足z是x,y的公共祖先中深度最大的那一个(即离他们最近的那一个)qwq
2.问题引入
看LCA之前最好学一下并查集,因为这两个东西有点相似,不同之处在于并查集一旦进行了路径压缩,便只能求出两个点之间是否存在关系,无法精确判断谁是谁的祖先以及两者的深度最大的公共祖先(只能判断有没有公共祖先)。
但LCA就不一样了,他可以实现并查集的操作,还可以查询两者的最近祖先,emm,关于这一点的应用嘛,就是比如说求树上最短路的问题,LCA会方便很多
3.LCA倍增算法本法
LCA的雏形就是单纯地考虑x,y同时向自己的爸爸跳,直到相遇,但如果是两条链连在树根上,x,y又分别是两个叶子结点的话。。。。会很慢很慢
那么问题来了:怎么优化???
不知道大家有没有想过我们为什么要使用树形结构?单纯是为了好看嘛?显然不是的。个人认为树形结构的优点之一在于:其特有的结构在搜索时可以极大地缩减深度,避免了一些不必要的试探
从而节约了时间。那么,当一棵树退化为一条链的时候,这个优点便不那么明显了,甚至会退化为普通的搜索算法。问题的关键在于我们在搜索时是一步一步地向前试探,但如果存在x,y的深度相差很
大的情况,那么,两个结点回溯到深度都为dep的祖先之前的试探其实都是无效的,这也是普通算法低效的原因所在。那么我们可不可以一次向上多跳几步呢?如果可以,我们应该怎么表示这种状态呢?
这里介绍一种倍增的思想,即每次向上跳2^k步,其中k为大于等于0的整数。不妨设x跳了2^k步后到达的结点为z,如果dep[z]>=dep[y],那么证明这一步试探也是无效的,但是否说明这个状态没有用呢?
其实不然。类似于快速幂的算法,x向上跳2^k步,其实等价于x先向上跳2^(k-1)步再向上跳2^(k-1)步,这也是我们为什么选择向上跳2的整数次幂步的原因,这是一种极其高效的方法,而且便于处理。
那么,我怎么知道向上跳2^k步后到达了哪个结点呢?这时候就需要预处理,不妨设f[x][k]表示x结点向上跳2^k步后所到达的结点,特别的,f[x][0]表示x的父结点那么由上述推论:f[x][k]=f[f[x][k-1]][k-1]
.然后我们再递归地向下处理。这就是预处理的部分.
然后,我们只需要先让x跳到与y深度相同(这里默认x的深度大于y),然后,x,y再同时向上跳,直到x,y相遇前的最后一步,那么,此时f[x][0]==f[y][0]==LCA(x,y)
整个算法也就结束啦,下面直接上代码:
题目:
给定一棵 n 个点的树,Q 个询问,每次询问点 x 到点 y两点之间的距离。
输入
第一行一个正整数 n ,表示这棵树有 n个节点;
接下来 n−1 行,每行两个整数 x,y 表示 x,y 之间有一条连边;
然后一个整数 Q,表示有Q 个询问;
接下来 Q 行每行两个整数x,y 表示询问 x 到 y 的距离。
输出
输出 Q 行,每行表示每个询问的答案。
样例输入
1 2
1 3
2 4
2 5
3 6
2
2 6
5 6
样例输出
4
提示
对于全部数据,1≤n,m≤105,1≤x,y≤n
程序来啦QAQ:
1 #include<iostream>
2 #include<cstdio>
3 #include<algorithm>
4 #include<cstdlib>
5 #define ONE 100010
6
7 using namespace std;
8
9 struct node {
10 int u;
11 int v;
12 int next;
13 };
14
15 node edge[ONE<<1]; //邻接表存数据
16 int dep[ONE],f[ONE][30],next[ONE],cnt_edge=0,n,q,x,y;
17
18 //int Read() 快读可以加速,可以学一下
19 //{
20 // int f=1,k=0;
21 // char c=getchar();
22 // while(c!='-'&&(c<'0'||c>'9')) c=getchar();
23 // if(c=='-')
24 // {
25 // f=-1;
26 // c=getchar();
27 // }
28 // while(c>='0'&&c<='9')
29 // {
30 // k=(k<<3)+(k<<1)+c-48;
31 // c=getchar();
32 // }
33 // return f*k;
34 //}
35
36 void add_edge(int u,int v)
37 {
38 edge[++cnt_edge].u=u;
39 edge[cnt_edge].v=v;
40 edge[cnt_edge].next=next[u];
41 next[u]=cnt_edge;
42 }
43
44 void Deal_first(int u,int fa)//预处理
45 {
46 dep[u]=dep[fa]+1;//处理当前结点深度,方便后面向上跳时使用
47
48 for(int i=0;i<=19;i++) f[u][i+1]=f[f[u][i]][i];//类似于二分的思想
49
50 for(int i=next[u];i;i=edge[i].next)
51 {
52 int to=edge[i].v;
53 if(to==fa) continue;//注意判断回边,不能跳到自己的父亲结点
54 f[to][0]=u;
55 Deal_first(to,u);//向下递归
56 }
57 }
58
59 int LCA(int x,int y) //求解LCA
60 {
61 if(dep[x]<dep[y]) swap(x,y); //保证x的深度大于y的深度
62 for(int i=20;i>=0;i--) //这里注意要先跳大步再跳小步,类似于用天平称量物体时先放大砝码再放小砝码是一个道理
63 {
64 if(dep[f[x][i]]>=dep[y]) x=f[x][i];
65
66 if(x==y) return x;
67 }
68 for(int i=20;i>=0;i--)
69 {
70 if(f[x][i]!=f[y][i])
71 {
72 x=f[x][i];
73 y=f[y][i];
74 }
75 }
76 return f[x][0];
77 }
78
79 int get_dis(int x,int y)
80 {
81 return dep[x]+dep[y]-2*dep[LCA(x,y)];
82 }
83
84 int main ()
85 {
86 //n=Read();
87 scanf("%d",&n);
88 for(int i=1;i<n;i++)
89 {
90 //x=Read();y=Read();
91 scanf("%d%d",&x,&y);
92 add_edge(x,y);
93 add_edge(y,x);
94 }
95
96 Deal_first(1,0);
97
98 //q=Read();
99 scanf("%d",&q);
100 while(q--)
101 {
102 // x=Read();
103 // y=Read();
104 scanf("%d%d",&x,&y);
105 printf("%d\n",get_dis(x,y));
106 }
107 return 0;
108 }
看完了点个赞再走哦亲~(手动开心o( ̄▽ ̄)o)
LCA树上倍增求法的更多相关文章
- [模板]LCA的倍增求法解析
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- Codevs 2370 小机房的树 LCA 树上倍增
题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...
- HDU 4822 Tri-war(LCA树上倍增)(2013 Asia Regional Changchun)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4822 Problem Description Three countries, Red, Yellow ...
- LCA树上倍增
LCA就是最近公共祖先,比如 节点10和11的LCA就是8,9和3的LCA就是3. 我们这里讲一下用树上倍增来求LCA. 大家都可以写出暴力解法,两个节点依次一步一步往上爬,直到爬到了相同的一个节点. ...
- 关于树论【LCA树上倍增算法】
补了一发LCA,表示这东西表面上好像简单,但是细节真挺多. 我学的是树上倍增,倍增思想很有趣~~(爸爸的爸爸叫奶奶.偶不,爷爷)有一个跟st表非常类似的东西,f[i][j]表示j的第2^i的祖先,就是 ...
- LCA——树上倍增
首先,什么是LCA? LCA:最近公共祖先 祖先:从当前点到根节点所经过的点,包括他自己,都是这个点的祖先 A和B的公共祖先:同时是A,B两点的祖先的点 A和B的最近公共祖先:深度最大的A和B的公共祖 ...
- 洛谷P3379lca,HDU2586,洛谷P1967货车运输,倍增lca,树上倍增
倍增lca板子洛谷P3379 #include<cstdio> struct E { int to,next; }e[]; ],anc[][],log2n,deep[],n,m,s,ne; ...
- 两种lca的求法:树上倍增,tarjan
第一种:树上倍增 f[x,k]表示x的2^k辈祖先,即x向根结点走2^k步达到的结点. 初始条件:f[x][0]=fa[x] 递推式:f[x][k]=f[ f[x][k-1] ][k-1] 一次bfs ...
- NOIP2013 货车运输 (最大生成树+树上倍增LCA)
死磕一道题,中间发现倍增还是掌握的不熟 ,而且深刻理解:SB错误毁一生,憋了近2个小时才调对,不过还好一遍AC省了更多的事,不然我一定会疯掉的... 3287 货车运输 2013年NOIP全国联赛提高 ...
随机推荐
- 使用Azure DevOps Pipeline实现.Net Core程序的CD
上一次我们讲了使用Azure DevOps Pipeline实现.Net Core程序的CI.这次我们来演示下如何使用Azure DevOps实现.Net Core程序的CD. 实现本次目标我们除了A ...
- 深入理解Java中的装箱与拆箱
一.Java数据类型 1.在说装箱与拆箱之前,先说一下Java的基本数据类型,Java从数据类型上可以划分为值类型与引用类型,值类型是四类八种,分别是: 整数型:byte̵,short̵,int̵,l ...
- Java链接db2套接字出错
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could ...
- Educational Codeforces Round 95(A-C题解)
A. Buying Torches 题目:http://codeforces.com/contest/1418/problem/A 题解:计算一个公式:1+n*(x-1)=(y+1)*k,求满足该条件 ...
- 面试题:JVM在Java堆中对对象的创建、内存结构、访问方式
一.对象创建过程 1.检查类是否已被加载 JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载.解析.初始化,若没有,则进行类加载 ...
- FTP服务器稳定性测试
FTP服务器稳定性探讨,如何部署FTP服务在server2003上,可能广大网友们有其他的选择,我选择的是Filezilla server.毕竟他是开源又免费 在架构师的悉心指导下,对FTP有了个更深 ...
- 关于windows服务器wsus客户端配置的修改
本文环境如下: 服务器:Windows Server 2012 R2 Standard 由于公司服务器是加了域环境的,又需要将wsus客户端指向另一台wsus服务器,修改本地组策略.可能会被域控给修改 ...
- Kubernetes K8S之Ingress详解与示例
K8S之Ingress概述与说明,并详解Ingress常用示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2C ...
- spring aop原理和实现
一.aop是什么 1.AOP面向方面编程基于IoC,是对OOP的有益补充: 2.AOP利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了 多个类的公共行为封装到一个可 ...
- 刷题[BJDCTF 2nd]简单注入
解题思路 打开发现登陆框,随机输入一些,发现有waf,然后回显都是同样的字符串.fuzz一波,发现禁了挺多东西的. select union 等 这里猜测是布尔盲注,错误的话显示的是:You konw ...