倍增法求LCA
1.前向星建图
2.预处理 i从1到n,lg[1]=0,如果i是2的幂,lg[i]=lg[i-1]+1,否则 lg[i]=lg[i-1].
3.一次dfs求各节点深度和倍增数组
4.lca查询 先跳到同一深度,若重合直接返回结果,否则同时往上跳,跳到lca下面一个位置,最后返回其父节点。
预处理O(nlog(n)),查询(log(n))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int to[1000010],nex[1000010],head[500010],par[500010][20],d[500010];
int lg[500010],lg1[500010];
void dfs(int now,int deep)
{
for(int i=1;i<=lg[deep];++i)
par[now][i]=par[par[now][i-1]][i-1];
d[now]=deep;
for(int i=head[now];i>0;i=nex[i])
{
if(to[i]==par[now][0])
continue;
par[to[i]][0]=now;
dfs(to[i],deep+1);
}
}
int lca(int x,int y)
{
if(d[x]<d[y])swap(x,y);
while(d[x]>d[y])
x=par[x][lg[d[x]-d[y]]];
if(x==y)
return x;
for(int i=lg[d[x]];i>=0;--i)
{
if(par[x][i]!=par[y][i])
{
x=par[x][i];
y=par[y][i];
}
}
return par[x][0];
}
int main()
{
int n,m,s,con=1;
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;++i)
{
int a,b;
scanf("%d%d",&a,&b);
to[con]=b;
nex[con]=head[a];
head[a]=con++;
to[con]=a;
nex[con]=head[b];
head[b]=con++;
}
lg[1]=0;
for(int i=2;i<=n;++i)
{
if((i&(i-1))==0)
lg[i]=lg[i-1]+1;
else
lg[i]=lg[i-1];
}
dfs(s,0);
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}
return 0;
}
Tarjan离线求LCA
1.将待查询的两点和查询标号存入vector中
2.运行Tarjan算法,在dfs的过程中未遍历的点st值为0,遍历未结束的点st为1,遍历结束的点st为2
3.先将点x的st值赋1,遍历x下的所有点,每个点遍历结束后要修改并查集祖先,然后在vector中查询与x有关的询问lca(x,y),如果st[y]==2,并查集中y的祖先就是lca(x,y).
4.将x的st值赋2.
时间复杂度O(n+m)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
typedef pair<int,int>pii;
const int N=1e4+10;
int h[N],nex[N*2],to[N*2],w[N*2],con=1,ans[2*N],st[N],p[N];
vector<pii>que[N];
int find(int x)
{return p[x]=p[x]==x?x:find(p[x]);}
void add(int a,int b,int k)
{
nex[con]=h[a];
h[a]=con;
to[con]=b;
w[con++]=k;
}
void tarjan(int x,int f)
{
st[x]=1;
for(int i=h[x];i;i=nex[i])
{
if(!st[to[i]])
{
tarjan(to[i],x);
p[to[i]]=x;
}
}
for(int i=0;i<que[x].size();++i)
{
int y=que[x][i].first;
if(st[y]==2)
{
int lca=find(y);
ans[que[x][i].second]=lca;
}
}
st[x]=2;
}
int main()
{
int n,m;cin>>n>>m;
for(int i=1;i<n;++i)
{
int x,y,k;scanf("%d%d%d",&x,&y,&k);
add(x,y,k),add(y,x,k);
}
for(int i=0;i<m;++i)
{
int x,y;scanf("%d%d",&x,&y);
if(x!=y)
{
que[x].push_back(pii(y,i));
que[y].push_back(pii(x,i));
}
}
for(int i=1;i<=n;++i)
p[i]=i;
tarjan(1,0);
for(int i=0;i<m;++i)
printf("%d\n",ans[i]);
return 0;
}