倍增法
这个思路比较简单
预处理
先从根节点dfs,找到每个结点的深度和fa[i][0](f[i][j]表示结点i的第级祖先)
在此过程中,同时可以确定每个结点的fa[i][j]
若1是根结点,fa[5][1] = fa[4][0] = 3,fa[5][2] = fa[3][1] = 1
可以看出 : fa[i][j] = fa[fa[i][j-1][j-1]
void dfs(int now, int fa){
dep[now] = dep[fa] + 1;
f[now][0] = fa;
for(int i = 1;(1 << i) <= dep[now]; i++)
f[now][i] = f[f[now][i-1]][i-1];
for(int i=head[now];i;i=edge[i].next){
int v=edge[i].v;
if(v!=fa) dfs(v,now);
}
return ;
}
倍增过程
假设求x,y的LCA,先把x,y跳到统一高度
之后两个点一块从大到小往上跳,只要fa[x][i] != fa[y][i],就令x = fa[x][i],y = fa[y][i],最终会跳到fa值不相同的最浅层的点,fa[x][0]即LCA
inline int find(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
int t = dep[x] - dep[y];
for(int i = 20;i >= 0;i --)
if((1 << i) & t)
x = f[x][i];
if(x == y) return y;
for(int i = 20;i >= 0;i --){
if(f[x][i] != f[y][i]){
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
板子题P3379 【模板】最近公共祖先(LCA)
代码
#include<bits/stdc++.h>
#define MAXN 500010
using namespace std;
int f[MAXN][30],dep[MAXN],head[MAXN],cnt=0,n,m,s;
struct node
{
int u,v,val,next;
}edge[MAXN<<2];
void add(int u,int v)
{
edge[++cnt].next=head[u];
head[u]=cnt;
edge[cnt].u=u;
edge[cnt].v=v;
return ;
}
void dfs(int now,int fa)
{
dep[now]=dep[fa]+1;
f[now][0]=fa;
for(int i=1;(1<<i)<=dep[now];i++)
f[now][i]=f[f[now][i-1]][i-1];
for(int i=head[now];i;i=edge[i].next)
{
int v=edge[i].v;
if(v!=fa)
dfs(v,now);
}
return ;
}
inline int find (int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
int t=dep[x]-dep[y];
for(int i=20;i>=0;i--)
if((t>>i)&1)
x=f[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int main()
{
cin >> n >> m >> s;
for(int i=1;i<=n-1;i++)
{
int u,v;
cin >> u >> v;
add(u,v);
add(v,u);
}
dfs(s,0);
while(m--)
{
int x,y;
cin>>x>>y;
cout<<find(x,y)<<endl;
}
return 0;
}
一些其他算法
Tarjan算法
tarjan是基于并查集的离线算法,预处理时间复杂度为O(n),单次查询为O(1)
至于代码。。。没学着写过
树链剖分
|