LCA与RMQ

一、什么是LCA?

LCA:Least Common Ancestors(最近公共祖先),对于一棵有根树T的任意两个节点u,v,求出LCA(T, u, v),即离跟最远的节点x,使得x同时是u和v的祖先。

二、算法分类

  求LCA的算法很多,按照是否在线可以分为在线算法和离线算法。
      在线算法:用比较长的时间做预处理,但是等信息充足以后每次回答询问只需要用比较少的时间。
      离线算法:先把所有的询问读入,然后一起把所有询问回答完成,不是本文所讲,Click here

三、在线算法

  (1)什么是RMQ?

  RMQ:给出一个数组A,回答询问RMQ(A, i, j),即A[i...j]之间的最值的下标。

  (2)RMQ算法,不是本文所讲,Click here

  (3)RMQ与LCA 

  假设一颗有根树,如图所示:

  LCA与RMQ

  我们可以通过深搜(从1节点开始)得到这样一个序列:

  欧拉序列V:1 2 1 3 4 3 5 6 5 7 5 3 1

  深度序列D:0 1 0 1 2 1 2 3 2 3 2 1 0

  First:1 2 4 5 7 8 10

  First表示节点i在欧拉序列V中第一次出现的位置

  

  现在比如我们要求LCA(4,7)

  那么找到节点4和7第一次出现的位置即:First(4)=5,First(7)=10

  对应到欧拉序列V中区间[5,10]即:4 3 5 6 5 7

  发现正好是以3为根的子树,然我我们找到这几个数中深度最小的即D(3),说明3就是4和7的最近公共祖先

  同理再比如说要求LCA(2,5),First(2)=2,First(5)=7,找到对应区间[2,7]即2 1 3 4 3 5

  然后找到深度最小的即1,说明1就是2和5的最近公共祖先。

四:例题

  HDU 2586

  AC CODE:

  

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
#define N 40010
#define M N*2 struct edge
{
int u,v,w,next;
}edge[M];
int tot,head[N]; int n,m;
int idx;
bool vis[N];
int ver[*N];
int dep[*N];
int first[N];
int dis[N];
int dp[*N][]; void init()
{
tot=;
memset(head,-,sizeof(head));
}
void add(int u,int v,int w)
{
edge[tot].u=u;
edge[tot].v=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
void init2()
{
idx=;
dis[]=;
memset(vis,,sizeof(vis));
}
void dfs(int u,int d)
{
vis[u]=;
ver[++idx]=u;
first[u]=idx;
dep[idx]=d;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(!vis[v]){
int w=edge[i].w;
dis[v]=dis[u]+w;
dfs(v,d+);
ver[++idx]=u;
dep[idx]=d;
}
}
}
void ST(int n)
{
int i,j;
for(i=;i<=n;i++) dp[i][]=i;
for(j=;(<<j)<=n;j++){
for(i=;i<=n;i++){
if(i+(<<j)-<=n){
int a=dp[i][j-];
int b=dp[i+(<<(j-))][j-];
dp[i][j]=dep[a]<dep[b]?a:b;
}
}
}
}
int RMQ(int i,int j)
{
int k=(int)(log((double)(j-i+))/log(2.0));
int a=dp[i][k],b=dp[j-(<<k)+][k];
return dep[a]<dep[b]?a:b;
}
int LCA(int u,int v)
{
int x=first[u];
int y=first[v];
if(x>y) swap(x,y);
int res=RMQ(x,y);
return ver[res];
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
init2();
dfs(,);
ST(*n-);
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
int lca=LCA(u,v);
printf("%d\n",dis[u]+dis[v]-*dis[lca]);
}
}
return ;
}
上一篇:JavaScript性能优化篇js优化


下一篇:javascript性能优化总结一(转载人家)