算法随笔——LCA

ST算法求解LCA

复杂度 O(nlogn)

引入欧拉序将树上问题转化为一条序列上的最值问题,id[u] 表示该节点在欧拉序中第一次出现的时间,vis[cnt] 存储下标。

欧拉序: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;
}

倍增

O(nlogn) 预处理 + O(logn) 查询

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

时间复杂度 O(n) 预处理 + O(logn) 查询

#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;
}
posted @   codwarm  阅读(19)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示