今天终于把倍增的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. 在SELECT DISTINCT 状况下使用 Order BY Newid() 随机数选出记录

    在日常作业中,有时候可能是一些活动要抽出得奖人或选出抽查的一些名单, 就常常会使用到 Order BY Newid() 的方式来做随机数选出, 但有可能的状况需是要搭配到 DISTINCT 来选出,这 ...

  2. mybatis 之 parameterType="Map"

    // 获得品牌下的商品 Map<String, Object> params = new HashMap<String, Object>(); params.put(" ...

  3. iOS - 选取相册中iCloud云上图片和视频的处理

    关于iOS选取相册中iCloud云上图片和视频  推荐看:TZImagePickerController的源码,这个是一个非常靠谱的相册选择图片视频的库 .当然也可以自己写 如下遇到的问题 工作原因, ...

  4. 《转》python学习(11)-表达式和语句

    转自 http://www.cnblogs.com/BeginMan/p/3164600.html 一.Python语句 if语句.else语句.elif语句.条件表达式.while语句.for语句. ...

  5. linux mutex

    #include <iostream> #include <queue> #include <cstdlib> #include <unistd.h> ...

  6. C++空类

    class Empty { public: Empty(); // 缺省构造函数 Empty( const Empty& ); // 拷贝构造函数 ~Empty(); // 析构函数 Empt ...

  7. 【大数据系列】HDFS初识

    一.HDFS介绍 HDFS为了做到可靠性(reliability)创建了多分数据块(data blocks)的复制(replicas),并将它们放置在服务集群的计算节点中(compute nodes) ...

  8. Android 屏幕适配:最全面的解决方案

    转自:https://www.jianshu.com/p/ec5a1a30694b 前言 Android的屏幕适配一直以来都在折磨着我们Android开发者,本文将结合: Google的官方权威适配文 ...

  9. 斐讯K2刷不死breed与第三方固件教程

    本文主要就是简单的斐讯 K2 刷机教程,方便大家了解一下 K2 怎样刷固件.斐讯 K2 是一款 1200M AC 双频无线路由器,支持 5G 和 2.4G WiFi 信号,虽然缺少 USB 且只有百兆 ...

  10. sencha touch 在线实战培训 第一期 第五节

    2014.1.6晚上8点准时开的课 本期培训一共八节,前三堂免费,后面的课程需要付费才可以观看. 本节内容:             实现皮肤自定义样式的修改 css相关理论: 重写官方样式,美化ap ...