【倍增法lca】dis

题目描述

给出n个点的一棵树,多次询问两结点之间的最短距离。(边是双向的)

输入

测试数据第一行为2个整数N和M(1<n<=10000,0<m<=20000).N表示点数,M表示询问次数。

下来n-1行,每行3个整数x,y,k,表示点x和点y之间存在一条边长为k(0<k<=100).

再接下来m行,每行2个整数x,y,表示询问点x到点y的最短距离

输出

输出m行。对于每次询问,输出一行询问结果。

样例输入

2 2
1 2 100
1 2
2 1

样例输出

100
100

题解

这题就是在原来的基础上加一个weight数组保存每个点到根节点的距离,输出的不再是深度而是距离。

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=5e5+10;
int num_edge=0;
int first[maxn],father[maxn],dep[maxn],weight[maxn],st[maxn][25];
struct Edge
{
	int next;
	int to;
	int dis;
}edge[maxn*2];
void add_edge(int from,int to,int dis)
{
	edge[++num_edge].next=first[from];
	edge[num_edge].to=to;
	edge[num_edge].dis=dis;
	first[from]=num_edge;
}
void dfs(int x,int fa)
{
	father[x]=fa;
	for(int i=first[x];i!=-1;i=edge[i].next)//遍历与x相连的所有边
	{
		int to=edge[i].to;
		if(to==fa)//防止形成环
			continue;
		dep[to]=dep[x]+1;
		weight[to]=weight[x]+edge[i].dis;
		dfs(to,x);
	}
}
int get_lca(int x,int y)
{
	if(dep[x]<dep[y])
		swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[st[x][i]]>=dep[y])
			x=st[x][i];
	if(x==y)
		return x;
	for(int i=20;i>=0;i--)
		if(st[x][i]!=st[y][i])
		{
			x=st[x][i];
			y=st[y][i];
		}
	return father[x];//return st[x][0]
}
int main()
{
	memset(first,-1,sizeof(first));
	memset(weight,0,sizeof(weight));
	int n,q;
	scanf("%d %d",&n,&q);
	for(int i=1;i<n;i++)
	{
		int x,y,z;
		scanf("%d %d %d",&x,&y,&z);
		add_edge(x,y,z);
		add_edge(y,x,z);
	}
	dep[1]=1;
	dfs(1,0);
	for(int i=1;i<=n;i++)
		st[i][0]=father[i];
	for(int j=1;j<=20;j++)
		for(int i=1;i<=n;i++)
			st[i][j]=st[ st[i][j-1] ][j-1];
	while(q--)
	{
		int x, y, z;
		scanf("%d %d", &x, &y);
		printf("%d\n",weight[x]+weight[y]-2*weight[get_lca(x,y)]);
	}
	return 0;
} 

 

### 倍增法求解最近公共祖先 LCA 的时间复杂度分析 #### 预处理阶段的时间复杂度 预处理阶段主要涉及构建倍增数组 `f[i][j]`,其中 `i` 表示节编号,`j` 表示跳跃步数对应的幂次。对于每一个节 i 和每一层 j,计算 f(i,j),即从节 i 开始向上跳 $2^j$ 步到达的节。此过程通过动态规划实现,在最坏情况下需要遍历树中的所有一次,并对每个节执行 O(log N) 次操作。 因此,预处理阶段的时间复杂度为 O(N log N)[^2]。 #### 查询阶段的时间复杂度 当进行单次查询时,假设要查找 u 和 v 个节之间LCA,则先将者的深度调整一致,再逐步比较它们在不同层次上的父辈节直到找到第一个相同的祖先为止。由于每次都是以指数级的速度上升(即每次都尝试向更高级别的祖先移动),所以整个过程中最多只需要做 O(log D) 次这样的比较动作,这里D代表树的最大深度。 综上所述,使用倍增方法解决 LCA 问题可以在 O(N log N) 时间内完成预处理工作;而在实际应用中针对每一对询问 (u,v),可以达到 O(log N) 的高效响应速度[^4]。 ```python def preprocess_lca(n, adj_list, root=0): import math max_level = int(math.log2(n)) + 1 parent = [[-1]*max_level for _ in range(n)] depth = [-1] * n stack = [(root, -1, 0)] while stack: node, par, dep = stack.pop() if par != -1: parent[node][0] = par depth[node] = dep for level in range(max_level-1): if parent[node][level] == -1: break parent[node][level+1] = parent[parent[node][level]][level] for child in adj_list[node]: if child != par: stack.append((child, node, dep+1)) return parent, depth def query_lca(u, v, parent, depth): if depth[u] < depth[v]: u, v = v, u diff = abs(depth[u]-depth[v]) for bit in reversed(range(len(parent[u]))): if diff & (1 << bit): u = parent[u][bit] if u == v: return u for bit in reversed(range(len(parent[u]))): if parent[u][bit] != parent[v][bit]: u = parent[u][bit] v = parent[v][bit] return parent[u][0] # Example usage: adjacency_list = [ [1], # Node 0 has one neighbor at index 1. [0, 2, 3], # Node 1 is connected to nodes 0, 2 and 3. [1], # ... [1], [] ] parent_table, depths = preprocess_lca(4, adjacency_list) print(query_lca(2, 3, parent_table, depths)) # Output should be '1' ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值