最近公共祖先模版

LCA tarjan 的离线算法

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 40010;
int first[maxn], head[maxn], cnt, sum;
struct edge
{
	int u, v, w, next;
}e[maxn*2], qe[maxn], Q[maxn];
int ans[maxn];
int f[maxn], vis[maxn];
int d[maxn];
void AddEdge(int u, int v, int w)
{
	e[cnt].u = u;
	e[cnt].v = v;
	e[cnt].w = w;
	e[cnt].next = first[u];
	first[u] = cnt++;
	e[cnt].u = v;
	e[cnt].v = u;
	e[cnt].w = w;
	e[cnt].next = first[v];
	first[v] = cnt++;
}

int find(int x)
{
	if(f[x] != x)
		return f[x] = find(f[x]);
	return f[x];
}
void LCA(int u, int k)
{
	f[u] = u;
	d[u] = k;
	vis[u] = true;
	for(int i = first[u]; i != -1; i = e[i].next)
	{
		int v = e[i].v;
		if(vis[v])
			continue;	
		LCA(v, k + e[i].w);
		f[v] = u;
	}
	for(int i = head[u]; i != -1; i = qe[i].next)
	{
		int v = qe[i].v;
		if(vis[v])
		{
			ans[qe[i].w] = find(v);
		}
	}
} 

LCA 转RMQ的在线算法

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200010;
struct edge
{
	int u, v, w, next;
}edges[maxn*2], e[maxn];

int E[maxn*2], H[maxn*2], I[maxn*2], L[maxn], R[maxn];
int dp[maxn*2][40];
int cnt, clock, dfn;
int first[maxn];
int a[maxn<<2];
int b[maxn];
int add[maxn<<2];
int degree[maxn];
int vis[maxn];
void AddEdge(int u, int v, int w)
{
	edges[cnt].u = u;
	edges[cnt].v = v;
	edges[cnt].w = w;
	edges[cnt].next = first[u];
	first[u] = cnt++;
	edges[cnt].u = v;
	edges[cnt].v = u;
	edges[cnt].w = w;
	edges[cnt].next = first[v];
	first[v] = cnt++;	
}
void dfs(int u, int fa, int dep)
{
	E[++clock] = u;
	H[clock] = dep;
	I[u] = clock;
	L[u] = ++dfn;
	b[dfn] = u;
	for(int i = first[u]; i != -1; i = edges[i].next)
	{
		int v = edges[i].v;
		if(v == fa)
			continue;
		if(vis[v])
			continue;
		vis[v] = true;
		dfs(v, u, dep+1);
		E[++clock] = u;
		H[clock] = dep;
	}
	R[u] = dfn;
}

void RMQ_init(int n)
{
	for(int i = 1; i <= n; i++)
		dp[i][0] = i;
	for(int j = 1; (1<<j) <= n; j++)
		for(int i = 1; i+(1<<j)-1 <= n; i++)
		{
			if(H[dp[i][j-1]] < H[dp[i+(1<<(j-1))][j-1]])
				dp[i][j] = dp[i][j-1];
			else
				dp[i][j] = dp[i+(1<<(j-1))][j-1];
		}
}
int RMQ(int l, int r)
{
	l = I[l], r = I[r];
	if(l > r)
		swap(l, r);
	int len = r-l+1, k = 0;
	while((1<<k) <= len)
		k++;
	k--;
	if(H[dp[l][k]] < H[dp[r-(1<<k)+1][k]])
		return E[dp[l][k]];
	else
		return E[dp[r-(1<<k)+1][k]];
}


LCA倍增法

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 20010;
const int INF = 999999999;
int anc[maxn][16], maxcost[maxn][16];
int fa[maxn], L[maxn], cost[maxn], vis[maxn];
int n, m;
int first[maxn], cnt;
struct edge
{
	int u, v, next;
}e[maxn*2];

void AddEdge(int u, int v)
{
	e[cnt].v = v;
	e[cnt].next = first[u];
	first[u] = cnt++;
	e[cnt].v = u;
	e[cnt].next = first[v];
	first[v] = cnt++;
}

void pre()
{
	for(int i = 1; i <= n; i++)
	{
		anc[i][0] = fa[i]; maxcost[i][0] = cost[i];
		for(int j = 1; (1<<j) < n; j++)
			anc[i][j] = -1;
	}
	for(int j = 1; (1<<j) < n; j++)
		for(int i = 1; i <= n; i++)
			if(anc[i][j-1] != -1)
			{
				int a = anc[i][j-1];
				anc[i][j] = anc[a][j-1];
				maxcost[i][j] = max(maxcost[i][j-1], maxcost[a][j-1]);
			}
	
}

int query(int p, int q)
{
	int tmp, log, i;
	if(L[p] < L[q])
		swap(p, q);
	for(log = 1; (1<<log) <= L[p]; log++);
	log--;
	
	int ans = -INF;
	for(int i = log; i >= 0; i--)
		if(L[p] - (1<<i) >= L[q])
		{
			ans = max(ans, maxcost[p][i]);
			p = anc[p][i];
		}
	if(p == q)
		return ans;
	for(int i = log; i >= 0; i--)
	{
		if(anc[p][i] != -1 && anc[p][i] != anc[q][i])
		{
			ans = max(ans, maxcost[p][i]);
			ans = max(ans, maxcost[q][i]);
			p = anc[p][i];
			q = anc[q][i];
		}
	}
	ans = max(ans, cost[p]);
	ans = max(ans, cost[q]);
	return ans;
}
void dfs(int u)
{
	for(int i = first[u]; i != -1; i = e2[i].next)
	{
		int v = e[i].v;
		if(vis[v])
			continue;
		vis[v] = 1;
		fa[v] = u;
		L[v] = L[u]+1;
		dfs(v);
	}
}


 

Tarjan算法是一种用于求解最近公共祖(Least Common Ancestors,LCA)问题的离线算法。算法的核心思想是利用深度优先搜索(DFS)和并查集(Union Find)来解决问题。 首先,我们从根节点开始遍历每一个节点,并将节点分为三类,用st[]数组表示。0代表还未被遍历,1代表正在遍历这个点,2代表已经遍历完这个点并且回溯回来了。这样的划分有助于确定节点的最近公共祖先。 在Tarjan算法中,我们一边遍历一边回应查询。每当遍历到一个节点时,我们查找与该节点相关的所有查询。如果查询中的节点已经被遍历完(即st[]值为2),我们可以利用已经计算好的信息来计算它们的最近公共祖先最近公共祖先的距离可以通过两个节点到根节点的距离之和减去最近公共祖先节点到根节点的距离来计算。 在Tarjan算法中,我们可以通过深度优先搜索来计算dist[]数组,该数组表示每个节点到根节点的距离。我们可以利用父节点到根节点的距离加上边的权值来计算每个节点到根节点的距离。 最后,我们可以通过并查集来操作st[]数组。当遍历完一个节点的所有子树后,将子树中的节点放入该节点所在的集合。这样,每个子树的节点的最近公共祖先都是该节点。 综上所述,Tarjan算法利用DFS和并查集来求解最近公共祖先问题。它的时间复杂度为O(n+m),其中n是节点数,m是查询次数。通过该算法,我们可以高效地解决最近公共祖先的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [最近公共祖先之tarjan](https://blog.csdn.net/qq_63092029/article/details/127737575)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【模版】Tarjan离线算法求最近公共祖先(LCA)](https://blog.csdn.net/weixin_43359312/article/details/100823178)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Tarjan算法求解最近公共祖先问题](https://blog.csdn.net/Yeluorag/article/details/48223375)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值