学了好久(一两个星期)都没彻底搞懂的lca,今天总算理解了。就来和大家分享下我自己的心得

首先,如果你还不懂什么是lca,出门左转自行百度

首先讲倍增

倍增的思想很简单,首先进行预处理,用一个深搜将每个点的深度和它向上跳一步到达的点(也就是它的父节点)处理出来,然后用下列递推式

f[i][j]=f[f[i][j-1]][j-1]

求出该点跳2^j步所到达的点。这里解释一下,为什么是f[f[i][]j-1][j-1]?因为倍增每次都是跳的2的整数次幂步,而2^j=2^(j-1)+2^(j-1);这样就不难理解了。

然后,对于每两个询问的点,只需要先找出那个点的深度更深,就将它跳跃到与另一个点深度相同,如果此时两个点相同,那么这个点就是最近公共祖先;如果不相同,两个点就一起跳,直找到最近公共祖先为止。


上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define N 500005
using namespace std;
int n,m,s,d[N],f[N][],head[N];
struct Edge{
int from,to,next;
}edge[N*];
inline int read()
{
char ch=getchar();int num=;
if(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<='')
{num=num*+ch-'';
ch=getchar();}
return num;
}
int anum=;
void add(int x,int y)
{edge[anum].to=y;
edge[anum].next=head[x];
head[x]=anum++;}
void dfs(int u)
{
for(int i=head[u];i!=-;i=edge[i].next)
{
int ne=edge[i].to;
if(d[ne]==)
{d[ne]=d[u]+;
f[ne][]=u;
dfs(ne);}
}
}
void init()
{
for(int i=;i<=;i++)
{
for(int j=;j<=n;j++)
{f[j][i]=f[f[j][i-]][i-];}
}
}
int lca(int a,int b)
{
if(d[a]<d[b]) swap(a,b);
for(int i=;i>=;i--)
{if(d[f[a][i]]>=d[b])
a=f[a][i];}
if(a==b) return a;
for(int i=;i>=;i--)
if(f[a][i]!=f[b][i])
a=f[a][i],b=f[b][i];
return f[a][];
}
int main()
{
memset(head,-,sizeof(head));
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);}
d[s]=;
dfs(s);init();
for(int i=;i<=m;i++)
{int a,b;
a=read();b=read();
printf("%d\n",lca(a,b));}
return ;
}

关于tarjan,具体思想我在另外一篇博客中已经讲过了,这里就只放代码,思路请转:这里

下面是代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define N 500005
#define M 1000001
using namespace std;
int n,m,s,cnt1,cnt2;
int dad[N],ans[N];
bool used[N];
struct edge{
int v,num,next;
}e1[M],e2[M];
struct road{
int head;
}v1[N],v2[N];
void before()
{
memset(v1,-,sizeof(v1));
memset(v2,-,sizeof(v2));
memset(used,false,sizeof(used));
memset(dad,-,sizeof(dad));
}
int find(int x)
{
return dad[x]==-?x:dad[x]=find(dad[x]);
}
void together(int x,int y)
{
int f1=find(x);
int f2=find(y);
if(f1!=f2)
dad[y]=x;
}
void v1add(int x,int y)
{
e1[cnt1].v=y;
e1[cnt1].next=v1[x].head;
v1[x].head=cnt1++;
}
void v2add(int x,int y,int z)
{
e2[cnt2].v=y;
e2[cnt2].num=z;
e2[cnt2].next=v2[x].head;
v2[x].head=cnt2++;
}
void tarjan(int u)
{
used[u]=true;
for(int i=v1[u].head;i!=-;i=e1[i].next)
{
int v=e1[i].v;
if(used[v]) continue;
tarjan(v);
together(u,v);
}
int sum;
for(int i=v2[u].head;i!=-;i=e2[i].next)
{
int v=e2[i].v;
sum=e2[i].num;
if(used[v])
ans[sum]=find(v);
}
}
int main()
{
int u,v;
scanf("%d%d%d",&n,&m,&s);
before();
int nn=n;
nn--;
while(nn--)
{
scanf("%d%d",&u,&v);
v1add(v,u);v1add(u,v);
}
for(int i=;i<=m;i++)
{
scanf("%d%d",&u,&v);
v2add(u,v,i);v2add(v,u,i);
}
tarjan(s);
for(int i=;i<=m;i++)
printf("%d\n",ans[i]);
return ;
}

LCA:倍增与tarjan的更多相关文章

  1. poj 1330 LCA (倍增+离线Tarjan)

    /* 先来个倍增 */ #include<iostream> #include<cstring> #include<cstdio> #define maxn 100 ...

  2. LCA算法解析-Tarjan&倍增&RMQ

    原文链接http://www.cnblogs.com/zhouzhendong/p/7256007.html UPD(2018-5-13) : 细节修改以及使用了Latex代码,公式更加美观.改的过程 ...

  3. 【codevs2370】小机房的树 LCA 倍增

    2370 小机房的树  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0 ...

  4. LCA倍增算法

    LCA 算法是一个技巧性很强的算法. 十分感谢月老提供的模板. 这里我实现LCA是通过倍增,其实就是二进制优化. 任何一个数都可以有2的阶数实现 例如16可以由1 2 4 8组合得到 5可以由1 2 ...

  5. 洛谷 3379 最近公共祖先(LCA 倍增)

    洛谷 3379 最近公共祖先(LCA 倍增) 题意分析 裸的板子题,但是注意这题n上限50w,我用的边表,所以要开到100w才能过,一开始re了两发,发现这个问题了. 代码总览 #include &l ...

  6. CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先)

    CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先) 题意分析 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天, ...

  7. POJ.1986 Distance Queries ( LCA 倍增 )

    POJ.1986 Distance Queries ( LCA 倍增 ) 题意分析 给出一个N个点,M条边的信息(u,v,w),表示树上u-v有一条边,边权为w,接下来有k个询问,每个询问为(a,b) ...

  8. POJ.1330 Nearest Common Ancestors (LCA 倍增)

    POJ.1330 Nearest Common Ancestors (LCA 倍增) 题意分析 给出一棵树,树上有n个点(n-1)条边,n-1个父子的边的关系a-b.接下来给出xy,求出xy的lca节 ...

  9. LCA(倍增在线算法) codevs 2370 小机房的树

    codevs 2370 小机房的树 时间限制: 1 s  空间限制: 256000 KB  题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点, ...

随机推荐

  1. 如何在WIndows电脑上安装 SVN Server 实现代码版本控制

    One 下载-安装 SVN SVNServer  先去官网下载服务器版本的svn server,下载地址 :https://www.visualsvn.com/server/download/   选 ...

  2. POJ 1113 Wall 凸包 裸

    LINK 题意:给出一个简单几何,问与其边距离长为L的几何图形的周长. 思路:求一个几何图形的最小外接几何,就是求凸包,距离为L相当于再多增加上一个圆的周长(因为只有四个角).看了黑书使用graham ...

  3. CentOS部署.NetCore服务

    1. 安装CentOs,可使用最小安装包镜像:http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-17 ...

  4. 10款好用的 jQuery 图片切换效果插件

    jQuery 是一个非常优秀的 Javascript 框架,使用简单灵活,同时还有许多成熟的插件可供选择.其中,最令人印象深刻的应用之一就是对图片的处理,它可以让帮助你在你的项目中加入一些让人惊叹的效 ...

  5. 用C++写一个没人用的ECS

    github地址:https://github.com/yangrc1234/Resecs 在做大作业的时候自己实现了一个简单的ECS,起了个名字叫Resecs. 这里提一下一些实现的细节,作为回顾. ...

  6. 2017ACM暑期多校联合训练 - Team 5 1001 HDU 6085 Rikka with Candies (模拟)

    题目链接 Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, s ...

  7. MSSQL 详解SQL Server连接(内连接、外连接、交叉连接)

    在查询多个表时,我们经常会用“连接查询”.连接是关系数据库模型的主要特点,也是它区别于其它类型数据库管理系统的一个标志. 什么是连接查询呢? 概念:根据两个表或多个表的列之间的关系,从这些表中查询数据 ...

  8. vue使用jsx/axios拦截器设置

    最害怕的就是做过的事情,转几天又忘记了:写过的代码,也模模糊糊不知道哪里去了,所以告诉自己最好把每天遇到的问题记录下来,好,开始. 新公司要搭个vue后台框架,所以用了简简单单的 vue+iview+ ...

  9. 执行impdp时出现的各种问题

    1.不同的表空间,不同的用户,不同的表名 impdp ODS_YYJC_BUF_ZB/ODS_YYJC_BUF_ZB job_name=bs3 directory=EXPDMP exclude=OBJ ...

  10. 【Python学习】request库

    Requests库(https://www.python-requests.org/)是一个擅长处理那些复杂的HTTP请求.cookie.header(响应头和请求头)等内容的Python第三方库. ...