今天终于把倍增的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——最近公共祖先的更多相关文章

  1. lca 最近公共祖先

    http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...

  2. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)

    Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...

  3. LCA(最近公共祖先)模板

    Tarjan版本 /* gyt Live up to every day */ #pragma comment(linker,"/STACK:1024000000,1024000000&qu ...

  4. CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )

    CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...

  5. LCA近期公共祖先

    LCA近期公共祖先 该分析转之:http://kmplayer.iteye.com/blog/604518 1,并查集+dfs 对整个树进行深度优先遍历.并在遍历的过程中不断地把一些眼下可能查询到的而 ...

  6. LCA 近期公共祖先 小结

    LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...

  7. LCA最近公共祖先 ST+RMQ在线算法

    对于一类题目,是一棵树或者森林,有多次查询,求2点间的距离,可以用LCA来解决.     这一类的问题有2中解决方法.第一种就是tarjan的离线算法,还有一中是基于ST算法的在线算法.复杂度都是O( ...

  8. Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】

    一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...

  9. (转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)

    基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个 ...

  10. LCA 最近公共祖先 tarjan离线 总结 结合3个例题

    在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...

随机推荐

  1. debug的一点总结

    程序员常常需要和bug打交道,一般来说调试bug的时间要多于编写程序的时间. bug可以简单的分为两大类: 语法上的bug 逻辑上的bug 语法上的bug就是指编译器能够识别的,例如常见的缺少分号和括 ...

  2. jquery easyui datagrid 无滚动条,datagrid 没垂直滚动条

    jquery easyui datagrid 无滚动条,datagrid 没垂直滚动条 ============================== 蕃薯耀 2018年2月6日 http://www. ...

  3. Git 学习笔记--Git下的冲突解决

    冲突的产生 很多命令都可能出现冲突,但从根本上来讲,都是merge 和 patch(应用补丁)时产生冲突. 而rebase就是重新设置基准,然后应用补丁的过程,所以也会冲突. git pull会自动m ...

  4. URL地址重写例子(Helicon)

    # Helicon ISAPI_Rewrite configuration file# Version 3.1.0.86 #RewriteEngine on RewriteRule ^/esf/.+( ...

  5. 【Linux】 Centos7 NC探测端口命令

    linux  centos7  测试端口的连通性,  分别测试TCP端口与UDP端口 1 这个需要Linux服务器里边支持nc命令,检查NC 是否安装 2  安装nc yum install nc - ...

  6. 《征服C指针》读书笔记

    本文同时发布在我的个人博客上,欢迎访问~ www.seekingdream.cn 在读完K&R之后,对C的认识就是指针.数组.网上的人们对指针也有些“敬而远之”的感觉.最近从同学处淘得< ...

  7. vue经验 - 实战疑点总结

    1.注册全局组件(是一个单vue页面组成的一个组件,而不是现拼的template结构) 结构: 代码:main.js import UserList from './components/UserLi ...

  8. PCL—低层次视觉—关键点检测(Harris)

    除去NARF这种和特征检测联系比较紧密的方法外,一般来说特征检测都会对曲率变化比较剧烈的点更敏感.Harris算法是图像检测识别算法中非常重要的一个算法,其对物体姿态变化鲁棒性好,对旋转不敏感,可以很 ...

  9. DRM in Android详解--转

    DRM,英文全称为Digital Rights Management,译为数字版权管理.它是目前业界使用非常广泛的一种数字内容版权保护技术.随着知识产权保护受重视的程度日益提高,快速攻城略地得Andr ...

  10. 改变presentModalView大小

    rc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; rc.modalPresentationStyle = UIModalP ...