求LCA——倍增

本代码写的也许有错误,欢迎各位大佬查找其中的错误(位运算不太会)。

  首先在这里不得不提的是——倍增是一种思想,没有一种特殊的框架或者模板之类的东西,有的人认为倍增竟然有模板这种东西。这与这种想法大佬给出的评论是好吧,也许这是来自大佬的嘲讽,或者是说大佬这在传递一种思想。

  倍增思想是一种十分巧妙的思想,在当今的信息学竞赛中应用得十分广泛。本质思想:每次根据已经得到的信息,将考虑的范围扩大一倍,从而加速操作。

  

  我们开始讲倍增求LCA:

      题目:口胡的——题意:给你一个森林,m个询问:v,p。求有多少个点(除v外) 与 v的第p个祖先相同?

    LCA指的是最近公共祖先(Least Common Ancestors),那怎么求呢?最粗鄙的方法就是先dfs一次,处理出每个点的深度,然后把深度更深的那一个点一个点地一个点地往上跳,直到到某个点和另外那个点的深度一样,然后两个点一起一个点地一个点地往上跳,直到到某个点(就是最近公共祖先)两个点“变”成了一个点。
    下面代码就诞生了:

 1 const int POW = 18;
 2 void dfs(int u , int fa){
 3     d[u] = d[fa] + 1;
 4     p[u][0] = fa;
 5     for(int i = 1; i < POW ; i ++) p[u][i] = p[p[u][i - 1]][i - 1];
 6     int sz = edge[u].size();
 7     for(int i = 0 ; i < sz ; i ++){
 8         int v = edge[u][i];
 9         if(v == fa) continue;
10         dfs(v , u);
11     }
12 }  

dfs    

不过有没有发现一个点地一个点地跳很浪费时间?如果一下子跳到目标点内存又可能不支持,相对来说倍增的性价比算是很高的,倍增的话就是一次跳2i 个点,不难发现深度差为x时,深度更深的那个点就需要跳x个点。

    我们采用倍增://a ^= b ^= a ^= b;此句感谢ZHT纠错 ; 非位运算乃RQY大佬所改

 1 int lca( int a , int b ){
 2     if( d[a] > d[b] ) a ^= b ^= a ^= b;
 3     if( d[a] < d[b] ){
 4         int del = d[b] - d[a];
 5         for( int i = 0; i < POW ; i ++ ) if(del & (1 << i)) b = p[b][i];
 6     }
 7     if( a != b ){
 8         for( int i = POW - 1; i >= 0; i -- )
 9             if( p[a][i] != p[b][i] )
10                  a = p[a][i] , b = p[b][i];
11         a = p[a][0] , b = p[b][0];
12     }
13     return a;
14 }  

位运算求 

1 if (d[a] > d[b]) a ^= b ^= a ^= b;
2   if (d[a] < d[b]) {
3     int del = d[a] - d[b];
4     int t = 1, i = 0;
5     while (t < del) t *= 2, ++i;
6     for (; t; t /= 2, --i) if (del >= t) { b = p[b][i]; del -= t; }
7   }
8   //...

非位运算

    注:上下合并一起看是完整代码;

时间: 2024-10-26 11:19:52

求LCA——倍增的相关文章

POJ 1986:Distance Queries(倍增求LCA)

http://poj.org/problem?id=1986 题意:给出一棵n个点m条边的树,还有q个询问,求树上两点的距离. 思路:这次学了一下倍增算法求LCA.模板. dp[i][j]代表第i个点的第2^j个祖先是哪个点,dp[i][0] = i的第一个祖先 = fa[i].转移方程:dp[i][j] = dp[dp[i][j-1][j-1]. 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm&g

poj1330Nearest Common Ancestors以及讲解倍增法求lca

Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20487   Accepted: 10784 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: In the figure, each node is labeled with an

倍增法求LCA

倍增法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 倍增法是通过一个数组来实现直接找到一个节点的某个祖先,这样我们就可以在O(logn)的时间内求出求出任意节点的任意祖先. 然后先把两个节点中转化为深度相同的节点,然后一起向上递增,知道找到相同的节点,该节点就是这两个节点的最近公共祖先. 代码实现: 1 #include<cstdio> 2 #include<iostream> 3 #define N

SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)

题意:给一个树图,每个点的点权(比如颜色编号),m个询问,每个询问是一个区间[a,b],图中两点之间唯一路径上有多少个不同点权(即多少种颜色).n<40000,m<100000. 思路:无意中看到树上莫队,只是拿来练练,没有想到这题的难点不在于树上莫队,而是判断LCA是否在两点之间的路径上的问题.耗时1天. 树上莫队的搞法就是: (1)DFS一次,对树进行分块,分成sqrt(n)块,每个点属于一个块.并记录每个点的DFS序. (2)将m个询问区间用所属块号作为第一关键字,DFS序作为第二关键字

HDU 4081 Qin Shi Huang&#39;s National Road System 最小生成树+倍增求LCA

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4081 Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5428    Accepted Submission(s): 1902 Problem Description

【LCA】倍增求LCA

题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每行包含两个正整数x.y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树). 接下来M行每行包含两个正整数a.b,表示询问a结点和b结点的最近公共祖先. 输出格式: 输出包含M行,每行包含一个正整数,依次为每一个询问的结果. 输入输出样例 输入样例#1: 复制 5 5 4 3 1 2 4

tarjan,树剖,倍增求lca

1.tarjan求lca Tarjan(u)//marge和find为并查集合并函数和查找函数 { for each(u,v) //访问所有u子节点v { Tarjan(v); //继续往下遍历 marge(u,v); //合并v到u上 标记v被访问过; } for each(u,e) //访问所有和u有询问关系的e { 如果e被访问过; u,e的最近公共祖先为find(e); } } 2.倍增lca(在线) #include<bits/stdc++.h> using namespace st

hdu 2586 How far away ? 倍增求LCA

倍增求LCA LCA函数返回(u,v)两点的最近公共祖先 #include <bits/stdc++.h> using namespace std; const int N = 40010*2; struct node { int v,val,next; node(){} node(int vv,int va,int nn):v(vv),val(va),next(nn){} }E[N]; int n,m; int tot,head[N],dis[N],f[N][20],dep[N]; void

【OI】倍增求LCA

╭(′▽`)╯ 总之,我们都知道lca是啥,不需要任何基础也能想出来怎么用最暴力的方法求LCA,也就是深度深的点先跳到深度浅的点的同一深度,然后一起向上一步步跳.这样显然太慢了! 所以我们要用倍增,倍增比较屌,直接2^k速度往上跳,而且复杂度和树剖lca差不多,那么步骤分为两步 1.让两个点到同一深度 2.到了同一深度同步往上跳 反正我一开始看的时候一直在想,万一跳过了怎么办?哈哈哈,所以说我们有办法嘛: 定义deepv为v点的深度,设两个要求lca的点分别为a,b,且deepa >= deep