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),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...
随机推荐
- polarssl rsa & aes 加密与解密
上周折腾加密与解密,用了openssl, crypto++, polarssl, cyassl, 说起真的让人很沮丧,只有openssl & polarssl两个库的RSA & AES ...
- MyBatis入门程序之Mapper代理方式
Mapper代理的开发方式,程序员只需要编写mapper接口(相当于dao接口)即可,MyBatis会自动为mapper接口生成动态代理实现类. 一.开发规范 1.mapper接口的全限定名要和map ...
- java命令启动jar包
本人对这些命令也是一知半解,记录备用. 1. 使用java命令行执行java文件 # 设置命令窗口标题 title test1 # 开启输出 @echo on # 设置环境变量JAVA_HOME se ...
- Web暴力破解--前端JS表单加密进行爆破
0x01 前言 常见的js实现加密的方式有:md5.base64.shal,写了一个简单的demo作为测试. 0x02 代码 login.html <!DOCTYPE HTML> < ...
- [PyCharm] 设置自动启动时自动打开项目
设置启动PyCharm时自动打开(或不打开)上次进行的项目: 选择 “Settings - General - Reopen last project on startup”,勾选该选项则启动时自动打 ...
- ORACLE常用函数汇总【转】
PL/SQL单行函数和组函数详解 函数是一种有零个或多个参数并且有一个返回值的程序.在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句, 函数主要分为两大类: 单行函数 ...
- Android 中的 Context
主要的功能是加载和访问资源(Context通常用来获取APP资源,创建UI,获取系统Service服务,启动Activity,绑定Service,发送广播,获取APP信息等) 如何理解: 我们可以理解 ...
- Android 本地tomcat服务器接收处理手机上传的数据之环境搭建
上一篇:Android 使用tomcat搭建HTTP文件下载服务器 本篇文章 环境:win7 + jdk1.7 + tomcat v8.0.53 工具: 1.Eclipse Eclips ...
- php 加密压缩
php 把文件打成压缩包 ,可以去搜下 pclzip 搜很好多地方没有找到对压缩包进行加密操作的. 如果服务器是linux 那么见代码: $filename="test.csv"; ...
- Java环境变量配置错误
1,由于Java的环境变量配置错误,导致用到Java的编译过程中出现错误: 改正办法: wget http://download.oracle.com/otn-pub/java/jdk/8u181-b ...