算法随笔——LCA
ST算法求解LCA
复杂度
引入欧拉序将树上问题转化为一条序列上的最值问题,
欧拉序:A B D B E F E G E B A C A
每访问一个节点都将其存入欧拉序。
例如求 D、C 的 LCA,其必定被夹在 D 和 C 各自第一次出现的位置之间,即 求解这一部分序列 D B E F E G E B A C 中深度最小的一点。
使用 ST 表离线求解。
// Problem: P3379 【模板】最近公共祖先(LCA)
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3379
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// Author: Eason
// Date:2023-10-28 15:22:13
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define il inline
#define gc getchar
inline int read()
{
int f=1,k=0;
char c = getchar();
while (c <'0' || c > '9')
{
if (c=='-') f=-1;
c=getchar();
}
while(c >= '0' && c <= '9') k = (k << 1)+(k << 3)+(c^48),c=getchar();
return k*f;
}
const int N = 1000005;
int n,m,s,h[N],e[N],idx,ne[N],cnt;
int id[N],f[N][25],vis[N],dep[N];
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs(int u,int fa,int dp)
{
id[u] = ++cnt;
vis[cnt] = u;
dep[cnt] = dp;
for (int i = h[u];i != -1;i = ne[i])
{
int j = e[i];
if (j != fa)
{
dfs(j,u,dp+1);
vis[++cnt] = u;
dep[cnt] = dp;
}
}
}
void prework()
{
for (int i = 1;i <= cnt;i++ )
f[i][0] = i;
for (int j = 1;j <= 25;j++)
for (int i = 1;(i+(1 << j)-1) <= cnt;i++)
{
int a = f[i][j-1],b = f[i+(1 << (j-1))][j-1];
if (dep[a] > dep[b]) f[i][j] = b;
else f[i][j] = a;
}
}
int ask(int aa,int bb)
{
int l = id[aa];
int r = id[bb];
if (l > r)
swap(l,r);
int k = log(r-l+1)/log(2);
int a = f[l][k];
int b = f[r-(1 << k)+1][k];
if (dep[a] > dep[b]) return vis[b];
else return vis[a];
}
int main()
{
memset(h,-1,sizeof h);
n = read(),m = read(),s = read();
for (int i = 1;i <= n-1;i++)
{
int x = read(),y = read();
add(x,y);add(y,x);
}
dfs(s,0,1);
prework();
while (m--)
{
int x = read(),y = read();
cout << ask(x,y) <<endl;
}
return 0;
}
倍增
void dfs(int x,int fat)
{
fa[x][0] = fat;
dep[x] = dep[fat] + 1;
for (int i = 1;i <= 20;i++) fa[x][i] = fa[fa[x][i-1]][i-1];
for (auto y:v[x])
{
if (y == fat) continue;
dfs(y,x);
}
}
int lca(int x,int y)
{
if (dep[x] < dep[y] ) swap(x,y);
while (dep[x] > dep[y])
{
x = fa[x][lg[dep[x]-dep[y]]-1];
}
if (x == y) return x;
for (int i = 20;i >= 0;i--) if (fa[x][i] != fa[y][i]) x = fa[x][i],y = fa[y][i];
return fa[x][0];
}
for (int i = 1;i <= N-2;i++) lg[i] = lg[i-1] + (1<<lg[i-1] == i);
树剖求lca
时间复杂度
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define re register
#define PII pair<int,int>
int read()
{
int f=1,k=0;char c = getchar();
while(c <'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')k=(k<<1)+(k<<3)+(c^48),c=getchar();
return k*f;
}
const int N = 5e5+5;
int n,m,s;
vector<int> v[N];
int dep[N];
int fa[N],son[N],siz[N],top[N];
int dfn[N],id[N],tot;
void dfs1(int x,int fat)
{
fa[x] = fat;
dep[x] = dep[fat] + 1;
siz[x] = 1;
int maxn = 0;
for (auto y :v[x])
{
if (y == fat) continue;
dfs1(y,x);
siz[x] += siz[y];
if (siz[y] > maxn) son[x] = y,maxn = siz[y];
}
}
void dfs2(int x,int tp)
{
top[x] = tp;
dfn[x]= ++tot;id[tot] = x;
if (son[x] ) dfs2(son[x],tp);
for (auto y :v[x])
if (y != son[x] && y != fa[x]) dfs2(y,y);
}
int query(int x,int y)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
if (dep[x] > dep[y]) return y;
return x;
}
int main()
{
cin >>n >> m >> s;
for (int i = 1;i <= n-1;i++)
{
int x= read(),y = read();
v[x].push_back(y);
v[y].push_back(x);
}
dfs1(s,0);
dfs2(s,s);
for (int i = 1;i <= m;i++)
{
int a= read(),b = read();
cout << query(a,b) <<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现