在线LCA一般大家都会用倍增吧,时间复杂度O(nlogn),空间复杂度O(nlogn),都是非常严格的复杂度限制,并且各种边界处理比较麻烦,有没有更快更好的办法呢?

我们发现,在树链剖分时,我们不经意的找到了LCA,能不能用这种方法找LCA呢?

答案是肯定的,使用轻重链剖分达到的LCA,时间复杂度最坏为O(logn),预处理是O(n)的dfs,比起每次处理严格O(nlogn),预处理O(nlogn)的倍增看起来好了很多,下面我们就用实验测量一下。

使用一个随机数据生成器生成了99组100000个点100000次询问的LCA,测试结果如下:

测试环境:intel I5-4200M 2.5GHz*2 windows7 VMware虚拟机

测试软件:cena 0.8

测试结果:

可以看到,树链剖分的代码比倍增有明显的优势,但是优势并不是特别大,平均每个点快了0.1秒左右。没有快太多的原因还是因为常数较大,劣处是代码量大了大约三十行。事实上,本人认为树链剖分比较好想,边界容易。

代码:

倍增:by Ommy_Zhang

 #include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 101010
#define MAXM 202020
#define TC 2000000
int n,m,u,v,k,lca,lastans;
int head[MAXN],next[MAXM],go[MAXM],cnt;
int father[MAXN][],deep[MAXN]; void add(int a,int b)
{
go[++cnt]=b;
next[cnt]=head[a];
head[a]=cnt;
}
void dfs(int x)
{
for(int k=;father[x][k];++k)
father[x][k+]=father[ father[x][k] ][k];
for(int e=head[x];e;e=next[e])
if(go[e]!=father[x][])
{
deep[go[e]]=deep[x]+;
father[go[e]][]=x;
dfs(go[e]);
}
}
int get_lca(int a,int b)
{
if(deep[a] < deep[b])
{
int t=a;
a=b;
b=t;
}
int d=deep[a]-deep[b];
for(int k=;k<;++k)
if((d>>k)&)
a=father[a][k];
if(a==b) return a; for(int k=;k>=;--k)
if(father[a][k]!=father[b][k])
{
a=father[a][k];
b=father[b][k];
}
return father[a][]; }
int main()
{
freopen ("LCA.in","r",stdin);
freopen ("LCA.out","w",stdout);
int n;
scanf("%d",&n);
int j,k;
for (int i=;i<n;++i)
{
scanf("%d%d",&j,&k);
add(j,k);
add(k,j);
}
dfs();
int m;
scanf("%d",&m);
for (int i=;i<=m;++i)
{
scanf("%d%d",&j,&k);
printf("%d\n",get_lca(j,k));
}
return ;
}

树链剖分:by SymenYang

#include <iostream>
#include <cstdio>
#include <algorithm>
#define maxn 100010
using namespace std;
struct edge
{
int to;
edge* next;
}ed[]; edge* head[];
int cnt=-;
void add(int j,int k)
{
edge* q=&ed[++cnt];
q->to=k;
q->next=head[j];
head[j]=q;
}
int fa[maxn];
int top[maxn];
int dep[maxn];
edge* wei[maxn];
int size[maxn];
void dfs(int now,int de,int last)
{
dep[now]=de;
size[now]=;
fa[now]=last;
int maxx=;
for (edge* q=head[now];q!=NULL;q=q->next)
{
if (q->to!=last)
{
dfs(q->to,de+,now);
if (size[q->to]>maxx)
{
wei[now]=q;
}
size[now]+=size[q->to];
}
}
return;
} void dfs2(int now,int last,int to)
{
top[now]=to;
if (wei[now])
dfs2(wei[now]->to,now,to);
for (edge* q=head[now];q!=NULL;q=q->next)
{
if (q->to!=last&&q!=wei[now])
{
dfs2(q->to,now,q->to);
}
}
return;
} int get_lca(int a,int b)
{
while (top[a]!=top[b])
{
if (dep[top[a]]<dep[top[b]]) a^=b^=a^=b;
a=fa[top[a]];
}
return dep[a]<dep[b]? a:b;
} int main()
{
freopen ("LCA.in","r",stdin);
freopen ("LCA.out","w",stdout);
int n;
scanf("%d",&n);
int j,k;
for (int i=;i<=n;++i) head[i]=NULL;
for (int i=;i<n;++i)
{
scanf("%d%d",&j,&k);
add(j,k);
add(k,j);
}
dfs(,,);
dfs2(,,);
int m;
scanf("%d",&m);
for (int i=;i<=m;++i)
{
scanf("%d%d",&j,&k);
printf("%d\n",get_lca(j,k));
}
return ;
}

每日算法——新型在线LCA的更多相关文章

  1. hdu3078 建层次树+在线LCA算法+排序

    题意:n个点,n-1条边构成无向树,每个节点有权,Q次询问,每次或问从a->b的最短路中,权第k大的值,/或者更新节点a的权, 思路:在线LCA,先dfs生成树0,标记出层数和fa[](每个节点 ...

  2. 在线LCA模板

    在线LCA 如求A,B两点的LCA,先计算出各个结点的深度d[],然后,通过递推公式求出各个结点的2次方倍的祖先p[],假设d[A] > d[B],则找到d[p[A][i]] == d[B]也就 ...

  3. hdu_4547_CD操作(在线LCA)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4547 题意:中文,不解释 题解:很裸的LCA,注意父目录打开子目录一次就够了,这里我才用倍增在线LCA ...

  4. 每日算法 - day 15

    每日算法 those times when you get up early and you work hard; those times when you stay up late and you ...

  5. Tarjan算法离线 求 LCA(最近公共祖先)

    本文是网络资料整理或部分转载或部分原创,参考文章如下: https://www.cnblogs.com/JVxie/p/4854719.html http://blog.csdn.net/ywcpig ...

  6. POJ 1330 Nearest Common Ancestors (LCA,倍增算法,在线算法)

    /* *********************************************** Author :kuangbin Created Time :2013-9-5 9:45:17 F ...

  7. ST(RMQ)算法(在线)求LCA

    在此之前,我写过另一篇博客,是倍增(在线)求LCA.有兴趣的同学可以去看一看.概念以及各种暴力就不在这里说了,那篇博客已经有介绍了. 不会ST算法的同学点这里 ST(RMQ)算法在线求LCA 这个算法 ...

  8. 【算法】RMQ LCA 讲课杂记

    4月4日,应学弟要求去了次学校给小同学们讲了一堂课,其实讲的挺内容挺杂的,但是目的是引出LCA算法. 现在整理一下当天讲课的主要内容: 开始并没有直接引出LCA问题,而是讲了RMQ(Range Min ...

  9. 利用Tarjan算法解决(LCA)二叉搜索树的最近公共祖先问题——数据结构

    相关知识:(来自百度百科)  LCA(Least Common Ancestors) 即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. 例如: 1和7的最近公共祖先为5: 1和5的 ...

随机推荐

  1. PHP 设计模式--基础

    设计模式的宗旨就是:重用. 在面向对象中,类是用于生成对象的代码模版,而设计模式是用于解决共性问题的代码模版. 遵循这样的模板,我们可以设快速地设计出优秀的代码. 注意,设计模式只是模板,不是具体的代 ...

  2. C解析config

    #cat bb.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include < ...

  3. hdu 4018 Parsing URL(字符串截取)

    题目 以下引用自百度百科: sscanf 的相关用法 头文件:#include<stdio.h>     1. 常见用法. 1 2 3 charbuf[512]; sscanf(" ...

  4. 37.分组聚合操作—其他metric

    课程大纲     要学其他的metric(count,avg,max,min,sum) count:bucket,terms,自动就会有一个doc_count,就相当于是count avg:avg a ...

  5. BZOJ 1051 HAOI 2006 受欢迎的牛

    [题解] 先用tarjan缩点,然后如果某个强联通分量的出度为0,则该强联通分量内的点数为答案,否则无解.因为若其他所有的强联通分量都有边连向Ai,则Ai必定没有出边,否则Ai连向的点所属的强联通分量 ...

  6. 平衡树前置——BST

    上一节:平衡树——序 BST(Binary Search Tree)二叉排序树,其定义为:二叉排序树或者是空树,或者是满足如下性质的二叉树: ①若它的左子树非空,则左子树上所有结点的值均小于根结点的值 ...

  7. 【Codeforces 1114D】Flood Fill

    [链接] 我是链接,点我呀:) [题意] 你选择一个point作为start_position 然后每次你可以将包含该start_position的所有联通块变成任意颜色 问你最少要多少次变换才能将所 ...

  8. 彻底搞定Android开发中软键盘的常见问题

    软键盘显示的原理 软件盘的本质是什么?软键盘其实是一个Dialog.        InputMethodService为我们的输入法创建了一个Dialog,并且将该Dialog的Window的某些参 ...

  9. 清北学堂模拟赛d4t4 a

    分析:感觉和dp的状态转移方式有点类似,对于一个数,你不能看有多少个状态能转移到它,你要看它能转移到多少个状态,相当于刷表法和填表法的区别,对于这道题也是一样,我们不能看有多少个数是x的倍数,而是每次 ...

  10. 树 (p155, 从中序和后续回复二叉树)

    递归求解, You are to determine the value of the leaf node in a given binary tree that is the terminal no ...