P4185 [USACO18JAN]MooTube G

本文介绍了一种结合离线思想与并查集的数据结构算法应用案例。通过将暴力算法离线化,实现高效的边权处理及点熟练度查询,解决了特定路径最小边权的问题。

知识点:离线,并查集

难度:5

这个运用的知识点是离线思想+并查集,怎么想到的,只要考虑出暴力的算法就很容易想到了,首先,我们点开洛谷右边相关讨论最上面的一个翻译,简洁的叙述了这个题目的意思,这样就不用自己看题了,定义两个点的熟练度是这两个点的路径上最小的边权,然后是给出询问,问到某点熟练度不小于某值的点的个数

我们考虑暴力的做法,每次询问,先初始化,每个点都自成一个连通块,然后按照题意,求到给定点熟练度不小于某值的点的个数,很自然的想到,这就等价于,我们把大于等于给定边权的边连接上,然后看要查询的点所在连通块的点的个数减一,就是这个询问我们要求的值,这很明显是正确的,因为当前查询点到其它连通块,肯定是要经过一条边权小于给定值的边的,那么那些连通块的点其实就不是答案,因为熟练度的定义是经过路径最小值,

想出来了暴力的做法,离线方法就很好想了,暴力的做法就是加边,离线就是我们记录输入的顺序,把所有的边按照大小顺序排序,把所有查询按照查询边权的大小排序,这样我们外层循环是按照边权大小处理查询,然后内层一个循环把大于等于当前查询边权的边加上,这样并不影响后面的查询,因为后面的查询边权是小于等于当前的,我们查到后面的时候,现在这些边权比较大的边肯定也是要连上的,最后按照顺序输出就行了,

这样除了并查集的操作这个题就变成1e5的线性的离线做法了,这个题自己没看题解10分钟估计就写出来了,还是很高兴的,但是这是并查集的练习题,提前知道这个是用并查集做的,至于离线的思想是链表的时候练过,这里才能想起来,发现并查集和离线思想的练习也不少

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

struct node {
	int x, y, z;
} a[N], b[N];

int fa[N], d[N], ans[N];

int get(int x) {
	if (x == fa[x]) return x;
	return fa[x] = get(fa[x]);
}

void merge(int x, int y) {
	int fx = get(x), fy = get(y);
	fa[fx] = fy;
	d[fy] += d[fx];
}

bool cmp1(node a, node b) {
	return a.z > b.z;
}

bool cmp2(node a, node b) {
	return a.x > b.x;
}

int main() {
	for (int i = 0; i < N; i++) {
		fa[i] = i;
		d[i] = 1;
	}
	int n, q;
	cin >> n >> q;
	for (int i = 1; i <= n - 1; i++) {
		cin >> a[i].x >> a[i].y >> a[i].z;
	}
	for (int i = 1; i <= q; i++) {
		cin >> b[i].x >> b[i].y;
		b[i].z = i;
	}
	sort(a + 1, a + n, cmp1);
	sort(b + 1, b + q + 1, cmp2);
	int ind = 1;
	for (int i = 1; i <= q; i++) {
		while (ind <= n - 1 && a[ind].z >= b[i].x) {
			merge(a[ind].x, a[ind].y);
			ind++;
		}
		ans[b[i].z] = d[get(b[i].y)] - 1;
	}
	for (int i = 1; i <= q; i++) cout << ans[i] << '\n';
	return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值