倍增LCA(bzoj 3732: Network)

本文解析了一道关于图论的竞赛题目,通过构建最小生成树并利用倍增法快速求解两点间路径上的最大边权值,介绍了算法实现的具体过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3732: Network

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1836   Solved: 868
[ Submit][ Status][ Discuss]

Description

给你N个点的无向图 (1 <= N <= 15,000),记为:1…N。 

图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000).

现在有 K个询问 (1 < = K < = 20,000)。 

每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Input

第一行: N, M, K。 
第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。 
第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

Output

 对每个询问,输出最长的边最小值是多少。

Sample Input

6 6 8
1 2 5
2 3 4
3 4 3
1 4 8
2 5 7
4 6 2
1 2
1 3
1 4
2 3
2 4
5 1
6 2
6 1

Sample Output

5
5
5
4
4
7
4
5


题目要两点之间最长的那条路尽可能的短

那么这条最短的“最长路”一定在最小生成树上,这个用反证法很好证明

这样就简单多了,因为是树,所以两点直接的路径唯一,每次只要查询路径中的最长边就好了

每次爆搜是不可能的,树链剖分又太麻烦(这都是废话)

倍增吧……


那。。怎么倍增?

先考虑暴力的方法:

先搜一遍预处理所有节点的父亲fa[x]和深度dep[x]

那么对于两点x和y,每次让更深的那个点向父亲爬就好了,最后两点一定会在最近公共祖先相遇,可这样超时

而倍增其实很简单,每个节点除了存下它的父亲以外,还存下它父亲的父亲、它父亲的父亲的父亲的父亲……

也就是说fa[x][j]存下x点的第2^j辈祖宗,例如fa[152][3]就是从节点152往上的第8个节点

递推式:fa[i][j] = fa[fa[i][j-1]][j-1];

那。。怎么爬捏?

步骤①:先让更深的那个节点往上爬,直到它们深度相同(有可能此时两个节点刚好相遇)

②:两个同时爬就好,如果它们的第2^j辈祖宗一样,则说明它们的最近公共祖先在第2^j之前;

如果它们的第2^j辈祖宗不一样,则让它们同时向上爬2^j步!

是不是很简单?


#include<stdio.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef struct Road
{
	int u, v;
	int len;
	bool operator < (const Road &b) const
	{
		if(len<b.len)
			return 1;
		return 0;
	}
}Road;
Road s[30005], temp;
vector<Road> G[15005];
int ufs[15005], fa[15100][20], bet[15100][20], deep[15100];
int Find(int x)
{
	if(ufs[x]==0)
		return x;
	return ufs[x] = Find(ufs[x]);
}
void Sech(int x)
{
	int i;
	deep[x] = deep[fa[x][0]]+1;				//预处理每个节点的深度和它的父亲
	for(i=0;i<G[x].size();i++)
	{
		temp = G[x][i];
		if(temp.v==fa[x][0])
			continue;
		fa[temp.v][0] = x;
		bet[temp.v][0] = temp.len;
		Sech(temp.v);
	}
}
int Query(int x, int y)
{
	int j, ans = 0;
	if(deep[x]<deep[y])
		swap(x, y);
	for(j=14;j>=0;j--)			//①:先让更深的那个节点往上爬,直到它们深度相同(有可能此时两个节点刚好相遇)
	{
		if(deep[fa[x][j]]>=deep[y])
		{
			ans = max(ans, bet[x][j]);
			x = fa[x][j];
		}
	}
	if(x==y)
		return ans;
	for(j=14;j>=0;j--)					//②:两个同时爬
	{
		if(fa[x][j]!=fa[y][j])
		{
			ans = max(ans, bet[x][j]);
			ans = max(ans, bet[y][j]);
			x = fa[x][j];
			y = fa[y][j];
		}
	}
	ans = max(ans, bet[x][0]);
	ans = max(ans, bet[y][0]);
	return ans;
}
int main(void)
{
	int n, m, k, i, j, t1, t2, x, y;
	scanf("%d%d%d", &n, &m, &k);
	for(i=1;i<=m;i++)
		scanf("%d%d%d", &s[i].u, &s[i].v, &s[i].len);
	sort(s+1, s+m+1);
	for(i=1;i<=m;i++)
	{
		t1 = Find(s[i].u);
		t2 = Find(s[i].v);
		if(t1!=t2)
		{
			ufs[t1] = t2;
			temp = s[i];
			G[s[i].u].push_back(temp);
			temp.u = s[i].v, temp.v = s[i].u;
			G[s[i].v].push_back(temp);
		}
	}
	Sech(1);
	for(j=1;j<=14;j++)
	{
		for(i=1;i<=n;i++)					//倍增
		{
			fa[i][j] = fa[fa[i][j-1]][j-1];
			bet[i][j] = max(bet[i][j-1], bet[fa[i][j-1]][j-1]);
		}
	}
	for(i=1;i<=k;i++)
	{
		scanf("%d%d", &x, &y);
		printf("%d\n", Query(x, y));
	}
}




内容概要:本文档详细介绍了Analog Devices公司生产的AD8436真均方根-直流(RMS-to-DC)转换器的技术细节及其应用场景。AD8436由三个独立模块构成:轨到轨FET输入放大器、高动态范围均方根计算内核和精密轨到轨输出放大器。该器件不仅体积小巧、功耗低,而且具有广泛的输入电压范围和快速响应特性。文档涵盖了AD8436的工作原理、配置选项、外部组件选择(如电容)、增益调节、单电源供电、电流互感器配置、接地故障检测、三相电源监测等方面的内容。此外,还特别强调了PCB设计注意事项和误差源分析,旨在帮助工程师更好地理解和应用这款高性能的RMS-DC转换器。 适合人群:从事模拟电路设计的专业工程师和技术人员,尤其是那些需要精确测量交流电信号均方根值的应用开发者。 使用场景及目标:①用于工业自动化、医疗设备、电力监控等领域,实现对交流电压或电流的精准测量;②适用于手持式数字万用表及其他便携式仪器仪表,提供高效的单电源解决方案;③在电流互感器配置中,用于检测微小的电流变化,保障电气安全;④应用于三相电力系统监控,优化建立时间和转换精度。 其他说明:为了确保最佳性能,文档推荐使用高质量的电容器件,并给出了详细的PCB布局指导。同时提醒用户关注电介质吸收和泄漏电流等因素对测量准确性的影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值