LCA——最近公共祖先
今天终于把倍增的LCA搞懂了!尽管周测都没写,尽管lca其实很简单,但这也是进度君的往前一点点的快乐。学渣的呻吟。
倍增的lca其实关键就在于二进制的二进制的拆分(显然是两次的拆分,很奇妙,懂二进制的自然不觉得什么)。把最关键的地方在这里列举一下吧:
1.f[fa][i]=f[f[fa][i-1]][i-1];类似于状态转移,i表示2^i可以表示fa的2的i次方的祖先所以当前的fa的2^i的祖先就是它2-1次方的祖先的2-1次方的祖先
想要理解为什么是这样,1+1=2;2+2=4;4+4=8;8+8=16;故2^i-1+2^i-1=2^i;到这里就可以理解了吧。
2.for(int i=t;i>=0;i--) 这里的t是t=(int)(log(n)/log(2))+1;的出来的n是最大的数目也就是log数,if(depth[f[y][i]]>=depth[x]) y=f[y][i];这里的是指假如当前的深度和x的深度比,能调到和x一样深就跳,这个代码完成了将y跳到x,二进制的拆分就是如此神奇!!!。(dalao勿喷蒟蒻没见过二进制拆分)
3.for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];这里的也就是x,y虽然到了同样的深度但是呢,公共祖先还是没有出来所以可以进行同时向上爬(我要一步一步向上爬)因为二进制的拆分所以最后都会达到距离最近公共祖先最近的子节点处,所以最后输出x的父亲节点就是x,y的公共子节点了,二进制的拆分就是如此神奇!!!。(dalao勿喷蒟蒻没见过二进制拆分)
这就是lca的具体之处认真思考其实都很简单(无奈作业太多)多思考啊。
复杂度为 O((n+m)logn)原因预处理处。
代码:
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<map>
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int maxn=<<;
const int maxx=;
int n,m,s,t;
int lin[maxn],nex[maxn],ver[maxn],len=;
int depth[maxx],f[maxx][];
void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
void dfs(int fa,int fath)
{
depth[fa]=depth[fath]+;
f[fa][]=fath;
for(int i=;i<=t;i++)
f[fa][i]=f[f[fa][i-]][i-];
for(int i=lin[fa];i;i=nex[i])
if(ver[i]!=fath)
dfs(ver[i],fa);
}
int lca(int x,int y)
{
if(depth[x]>depth[y])
swap(x,y);
for(int i=t;i>=;i--)
if(depth[f[y][i]]>=depth[x])
y=f[y][i];
if(x==y)
return x;
for(int i=t;i>=;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][];
}
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();s=read();
t=(int)(log(n)/log())+;
for(int i=;i<n;i++)
{
int x,y;
x=read();y=read();
add(x,y);add(y,x); }
dfs(s,);
for(int i=;i<=m;i++)
{
int x,y;
x=read();y=read();
printf("%d\n",lca(x,y));
}
return ;
}
学长说求lca 方法有:倍增(如上),tanjan(如下),st(不懂),树剖(不会)。
那就浅谈tanjan离线来求lca吧。主要运用并查集的回溯的神奇效应,很好懂得。
当前当前节点回溯时查询问题点是否回溯过,一旦回溯过就有父亲了。没有的话让另一个对应点来更新具体细节看代码。
#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<queue>
#include<deque>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline void put(int x)
{
if(x==){putchar('');putchar('\n');return;}
if(x<)putchar('\n'),x=-x;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
const int maxn=;
vector<int>q[maxn],q1[maxn];
int n,m,s;
int f[maxn],vis[maxn],ans[maxn];//存答案
int ver[maxn<<],nex[maxn<<],lin[maxn<<],len=;
void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
int getfather(int x){return f[x]==x?x:f[x]=getfather(f[x]);}
void tarjan(int x)
{
vis[x]=;
for(int i=lin[x];i;i=nex[i])
{
int tn=ver[i];
if(vis[tn]!=)continue;//防止便利到父亲啊
//等价于 if(vis[tn]==1)continue;因为儿子没回溯父亲也不可能回溯
tarjan(tn);
f[tn]=x;
vis[tn]=;//直接标记回溯过了
}
for(int i=;i<q[x].size();i++)
{
int tn=q[x][i];
if(vis[tn]==)//如果对应点回溯过那么父亲就有了!
{
ans[q1[x][i]]=getfather(tn);
}
}
//vis[x]=2;这里标记回溯当然也是可以的啦
//因为对x是否回溯对当前x节点的对应点询问和x是否回溯过无关所以可以进行上述面的标记 }
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();s=read();
for(int i=;i<n;i++)
{
int x,y;
x=read();y=read();
add(x,y);add(y,x);
}
for(int i=;i<=n;i++)f[i]=i;
for(int i=;i<=m;i++)
{
int x,y;
x=read();y=read();
q[x].push_back(y);q[y].push_back(x);
q1[x].push_back(i);q1[y].push_back(i);//为了O(n)输出答案而准备
}
tarjan(s);
for(int i=;i<=m;i++)put(ans[i]);
return ;
}
复杂度 O(n+2*m)-->O(n+m);
高歌取醉欲自慰,起舞落日争光辉。
LCA——最近公共祖先的更多相关文章
- lca 最近公共祖先
http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...
- Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)
Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...
- LCA(最近公共祖先)模板
Tarjan版本 /* gyt Live up to every day */ #pragma comment(linker,"/STACK:1024000000,1024000000&qu ...
- CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )
CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...
- LCA近期公共祖先
LCA近期公共祖先 该分析转之:http://kmplayer.iteye.com/blog/604518 1,并查集+dfs 对整个树进行深度优先遍历.并在遍历的过程中不断地把一些眼下可能查询到的而 ...
- LCA 近期公共祖先 小结
LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...
- LCA最近公共祖先 ST+RMQ在线算法
对于一类题目,是一棵树或者森林,有多次查询,求2点间的距离,可以用LCA来解决. 这一类的问题有2中解决方法.第一种就是tarjan的离线算法,还有一中是基于ST算法的在线算法.复杂度都是O( ...
- Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】
一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...
- (转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)
基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个 ...
- LCA 最近公共祖先 tarjan离线 总结 结合3个例题
在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...
随机推荐
- centos 上不了网了
昨天还用的好好的,今天就上不了网了,郁闷,不过,正好是一次学习linux网络配置的好机会,这会已经把它折腾好了,此文就是在linux下面的浏览器中写的! 先检查一下虚拟机中的网络设置是否正常,由于我的 ...
- iOS开发-NSDictionary
判断一个字典中是否存在某个key,有两种方法: 方法一: if ([dictionary allKeys] containsObject: key]){ // cotains key operatio ...
- fork(), waitpid()
NAME fork - create a child process SYNOPSIS #include <unistd.h> pid_t fork(void); RETURN VALUE ...
- es 批量导入文件
首先是json格式的文件: curl -XPOST 'localhost:9200/bank/account/_bulk?pretty' --data-binary @accounts.json 1 ...
- ubuntu MySQL采用apt-get install安装目录
一). ubuntu下mysql安装布局: /usr/bin 客户端程序和mysql_install_db /var/lib/mysql ...
- android模拟器与PC的端口映射
一.概述 Android系统为实现通信将PC电脑IP设置为10.0.2.2,自身设置为127.0.0.1,而PC并没有为Android模拟器系统指定IP,所以PC电脑不能通过IP来直接访问Androi ...
- 《MySQL》一次MySQL慢查询导致的故障
本文转载自 http://www.jb51.net/article/70955.htm 我们知道分析MySQL语句查询性能的方法除了使用EXPLAIN 输出执行计划,还可以让MySQL记录下查询超过指 ...
- shell编程(一)
迷迷糊糊中发现了一个学习shell的非常好的教程,从头到尾看了一下,等看完全忘记了,没办法只能记录下来,教程网址http://c.biancheng.net/cpp/view/6994.html 以前 ...
- 推荐系统之基于图的推荐:基于随机游走的PersonalRank算法
转自http://blog.csdn.net/sinat_33741547/article/details/53002524 一 基本概念 基于图的模型是推荐系统中相当重要的一种方法,以下内容的基本思 ...
- 计算完成率 SQL
计算完成率 SQL ,), ,) ) AS XX_完成率