LCA
概念
最近公共祖先
模板
题目描述
给定n个点的树(1是根),m次询问,每次询问两点的LCA;
输入格式
第一行两个整数,n,m;
接下来n-1行,给定正整数a,b,表示a,b间有边;
接下来m行,给定整数a,b,表示询问a,b;
输出格式
对于每个询问,输出它们的LCA;
样例数据
input
3 2
1 2
2 3
1 2
2 3
output
1
2
数据规模与约定
保证所有数据n,m<=100000
方法1(tarjan“向上标记法”)
dfs一条路搜到底时,搜到了起点,在回溯返回时每次将子节点通过并查集合并到父节点上,当搜到终点时,那么father[起点]就是起点和终点的转折点LCA。
code1
#include<bits/stdc++.h>
using namespace std;
int n,m,t1,t2,s;
struct edge
{
int y,next,num;
}a1[1000010];
edge a2[1000010];
bool vis[500010]={};
int father[500010]={},ans[500010]={},link1[500010]={},link2[500010]={};
void insert1(int x,int y){a1[++t1].y=y;a1[t1].next=link1[x];link1[x]=t1;}
void insert2(int x,int y,int num){a2[++t2].y=y;a2[t2].next=link2[x];link2[x]=t2;a2[t2].num=num;}
void init()
{
scanf("%d%d%d",&n,&m,&s);
int x,y;
for(int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
insert1(x,y);
insert1(y,x);
father[i]=i;
}
father[n]=n;
for(int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
insert2(x,y,i);
insert2(y,x,i);
}
}
int getfa(int x)
{
return x==father[x] ? x:father[x]=getfa(father[x]);
}
void dfs(int x,int fa)
{
vis[x]=1;
for(int i=link2[x];i;i=a2[i].next)
if (vis[a2[i].y])
ans[a2[i].num]=getfa(a2[i].y);
for(int i=link1[x];i;i=a1[i].next)
if (a1[i].y!=fa)
dfs(a1[i].y,x);
father[x]=fa;
}
int main()
{
init();
dfs(s,0);
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
return 0;
}
方法2(RMQ树上倍增法)
需要一个数组F[i][j],表示i节点之前第2^j号祖先。当然,F[i][0]即是i的父节点。
F[i][j]==F[F[i][j-1]][j-1].
那么可以O(nlogn)预处理出数组F
void prepare()
{
int fence=log(1.0*(max_dep))/log(2.0);
p[1][0]=-1;
for(int j=1;j<=fence;j++)
for(int i=1;i<=n;i++)
if(p[i][j-1]!=-1)
p[i][j]=p[p[i][j-1]][j-1];
}
对于每个起点a,终点b。
先求出他们的深度数组与F数组
每次求LCA将两个点深度调为一致。然后可以通过一起向上跳的方法求出LCA。
int LCA(int a,int b)
{
if(a==b) return a;
if(dep[b]>dep[a]) swap(a,b);
int D;
for(D=0;(1<<D)<=dep[a];D++);
D--; //确定a的最大2次幂深度;
for(int j=D;j>=0;j--)
if(dep[a]-(1<<j)>=dep[b])
a=p[a][j]; //令a与b深度相同;
if(a==b) return a;
for(int j=D;j>=0;j--)
if(p[a][j]!=-1 && p[a][j]!=p[b][j])
a=p[a][j],b=p[b][j];
return p[a][0];
}
code
#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,t=0;
int p[100010][50]={},dep[100010]={};
struct note
{
int y,next;
}e[200010]={};
int linkk[100010]={};
void insert(int x,int y)
{
e[++t].y=y;
e[t].next=linkk[x];
linkk[x]=t;
}
void dfs(int x,int father)
{
p[x][0]=father;
for(int i=linkk[x];i;i=e[i].next)
if (e[i].y!=father)
dep[e[i].y]=dep[x]+1,dfs(e[i].y,x);
}
void make_()
{
int xx=log(1.0*n)/log(2.0);
for(int j=1;j<=xx;++j)
for(int i=1;i<=n;++i)
if (p[i][j-1]!=-1)
p[i][j]=p[p[i][j-1]][j-1];
}
int LCA(int xx,int yy)
{
if (xx==yy) return xx;
if (dep[xx]<dep[yy]) swap(xx,yy);
int D;
for(D=0;(1<<D)<=dep[xx];++D);
--D;
for(int i=D;i>=0;--i)
if (dep[xx]-(1<<i)>=dep[yy])
xx=p[xx][i];
if (xx==yy) return xx;
for(int i=D;i>=0;--i)
if (p[xx][i]!=-1&&p[xx][i]!=p[yy][i])
xx=p[xx][i],yy=p[yy][i];
return p[xx][0];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
insert(x,y);
insert(y,x);
}
dfs(1,-1);
make_();
for(int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
printf("%d\n",LCA(x,y));
}
return 0;
}