kuangbin带你飞 并查集专题

A - Wireless Network

一开始觉得会超时,后来发现时限是10s,就直接开始暴力

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e3 + 5;

int fa[N]; // 记录每个节点的父节点
int x[N], y[N];
bool rep[N];
double dis[N][N]; 

double cal(int i, int j)
{
    
    
	return sqrt(pow(x[i] - x[j], 2.0) + pow(y[i] - y[j], 2.0));
}

void init(int n)
{
    
    
	for (int i = 1; i <= n; i++)
		fa[i] = i;
}

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

void unite(int x, int y)
{
    
    
	x = find(x), y = find(y);
	if (x == y) return;
	fa[x] = y;
}

int main(void)
{
    
    
	int t, n, d;
	int p, q;
	
	scanf("%d%d", &n, &d);
	init(n);
	
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d%d", &x[i], &y[i]);
	}
	for (int i = 1; i <= n; i++) {
    
    
		for (int j = i + 1; j <= n; j++) {
    
    
			dis[i][j] = dis[j][i] = cal(i, j);
		}
	}
	char op[2];
	while (scanf("%s", op) != EOF){
    
    
		if (op[0] == 'O') {
    
    
			scanf("%d", &p);
			rep[p] = 1;
			for (int i = 1; i <= n; i++) {
    
    
				if (i != p && rep[i] == 1 && dis[i][p] <= d) {
    
    
					unite(p, i);
				}
			}
		} else {
    
    
			scanf("%d%d", &p, &q);
			if (find(p) == find(q)) {
    
    
				puts("SUCCESS");
			} else {
    
    
				puts("FAIL");
			}
		}
	}
	
	return 0;
}

B - The Suspects

0号为疑似患者,已知有m个群体,和疑似患者同属一个群体的人都是疑似患者,求有多少疑似患者

模板题,合并后求有多少个人和0号在一个群体内

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;

int fa[N]; // 记录每个节点的父节点

void init(int n)
{
    
    
	for (int i = 0; i < n; i++)
		fa[i] = i;
}

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

void unite(int x, int y)
{
    
    
	x = find(x), y = find(y);
	if (x == y) return;
	fa[x] = y;
}

int main(void)
{
    
    
	int n, m;
	int k, x, y;
	
	while (scanf("%d%d", &n, &m) != EOF && n) {
    
    
		init(n);
		while (m--) {
    
    
			scanf("%d", &k);
			for (int i = 0; i < k; i++) {
    
    
				if (i == 0)
					scanf("%d", &x);
				else
					scanf("%d", &y);
				if (i != 0) {
    
    
					unite(x, y);
				}
			}
		}
		int res = 0;
		for (int i = 0; i < n; i++) {
    
    
			if (find(i) == find(0)) {
    
    
				res++;
			}
		}
		printf("%d\n", res);
	}
	
	return 0;
}

C - How Many Tables

并查集模板题,合并后求有多少个根节点

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 5e4 + 5;

int fa[N]; // 记录每个节点的父节点

void init(int n)
{
    
    
	for (int i = 1; i <= n; i++)
		fa[i] = i;
}

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

void unite(int x, int y)
{
    
    
	x = find(x), y = find(y);
	if (x == y) return;
	fa[x] = y;
}

int main(void)
{
    
    
	int t, n, m;
	int x, y;
	scanf("%d", &t);
	while (t--) {
    
    
		scanf("%d%d", &n, &m);
		init(n);
		for (int i = 0; i < m; i++) {
    
    
			scanf("%d%d", &x, &y);
			unite(x, y);
		}
		int ans = 0;
		for (int i = 1; i <= n; i++)
			if (find(i) == i)
				ans++;
		printf("%d\n", ans);
	}
	return 0;
}

D - How Many Answers Are Wrong

带权并查集,利用边权的差来表示区间和

d 数组表示权值,因为给出的是一段区间的两个端点 a、b 和 [a, b] 的区间和,我们要找到 a、b 的一个共同基准点,再使用 d[b] - d[a - 1] 来表示 [a, b] 的区间和。

如何找到一个共同的基准点呢?

可以把每次给出的节点 a - 1 和节点 b 进行合并,此时 a - 1 和 b 就有一个共同的基准点(即它们的根节点),所以就可以巧妙的利用它们边权的差来表示区间和

这里主要讲一下带权并查集的 unite 操作:

已知:fa[x] = fx, fa[x] = fy
在这里插入图片描述
合并节点 x 和 y 后,使 fy 作为 fx 的父节点。

在这里插入图片描述

题目中给出 x 到 y 的距离为 s

有两条路径从 x 到 fy:

1、x ——> fx ——> fy

2、x ——> y ——> fy

这两条路的权值是相等的,即:d[x] + d[fx] = s + d[y]

因为之前合并了 fx 和 fy,fx 作为子节点,根据上式可以更新 d[fx] = d[y] - d[x] + s

所以每次读入 a b 后,先判断 a b 是否同属一个集合:

1、如果属于一个集合,再判断 d[b] - d[a - 1] 是否等于 s,如果不等于,说明回答是错误的。

2、如果不属于一个集合,就合并 a - 1 和 b 两个集合

一位大佬的博客:https://blog.csdn.net/TheSunspot/article/details/107768182

#include <cstdio>
#include <cmath>
using namespace std;
const int N = 2e5 + 5;

int fa[N], d[N];

void init(int n)
{
    
    
	for (int i = 0; i <= n; i++) {
    
    
		fa[i] = i;
		d[i] = 0;
	}
}

int find(int x)
{
    
    
	if (x == fa[x]) return x;
	int root = find(fa[x]); 
	d[x] += d[fa[x]];
	return fa[x] = root;
}

void unite(int x, int y, int s)
{
    
    
	int fx = find(x), fy = find(y);
	fa[fx] = fy; d[fx] = d[y] - d[x] - s;
}

int main(void)
{
    
    
	int n, m;
	int a, b, s;
	int res;
	while (scanf("%d%d", &n, &m) != EOF) {
    
    
		init(n);
		res = 0;
		while (m--) {
    
    
			scanf("%d%d%d", &a, &b, &s);
			if (find(a - 1) == find(b) && d[b] - d[a - 1] != s) {
    
    
				res++;
			} else {
    
    
				unite(a - 1, b, s);
			}
		}
		printf("%d\n", res);
	}
	
	return 0;
}

G - Supermarket

贪心+并查集

贪心策略:先选取利润最大的商品,如果利润相等,优先选择截止时间长的商品

以每个时间点为根,最初时间点 x 是根节点,说明 x 没有被占用,则可以使用时间点 x。使用之后,合并 x 和 x - 1 所在的集合。此时 x 的根节点会变成 x-1,下次再进行 find 操作,会找到 x-1。依次类推,直到最后没有可用的时间点。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10005;

int fa[N];

struct pro{
    
    
	int p, t;
}arr[N];

void init(int n)
{
    
    
	for (int i = 0; i <= n; i++)
		fa[i] = i;
} 

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

void unite(int x, int y)
{
    
    
	x = find(x), y = find(y);
	if (x == y) return;
	fa[x] = y;
}

bool cmp(pro a, pro b)
{
    
    
	if (a.p == b.p)
		return a.t > b.t;
	return a.p > b.p;
}

int main(void)
{
    
    
	int n, res, maxv, root;
	while (scanf("%d", &n) != EOF) {
    
    
		maxv = res = 0;
		for (int i = 0; i < n; i++) {
    
    
			scanf("%d%d", &arr[i].p, &arr[i].t);
			maxv = max(maxv, arr[i].t);
		}
		init(maxv);
		sort(arr, arr + n, cmp);
		
		for (int i = 0; i < n; i++) {
    
    
			root = find(arr[i].t);
			if (root > 0) {
    
    
				res += arr[i].p;
				fa[root] = root - 1;
			}
		}
		printf("%d\n", res);
	}
	
	return 0;
}

J - A Bug’s Life

用一个opp数组,来记录每个虫子的异性。

看每次输入的两只虫子是否在一个集合里,如果不在,说明为异性,如果在一个集合里,说明是同性。

已知异性的异性为同性,所以可以将虫子和异性的异性进行合并。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 20005;

int fa[N], opp[N];

void init(int n)
{
    
    
	for (int i = 1; i <= n; i++) {
    
    
		fa[i] = i;
		opp[i] = 0;
	}
} 

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

void unite(int x, int y)
{
    
    
	x = find(x), y = find(y);
	if (x == y) return;
	fa[x] = y;
}

int main(void)
{
    
    
	int t, n, m;
	int a, b, flag;
	scanf("%d", &t);
	for (int idx = 1; idx <= t; idx++) {
    
    
		scanf("%d%d", &n, &m);
		init(n);
		flag = 0;
		while (m--) {
    
    
			scanf("%d%d", &a, &b);
			if (find(a) == find(b)) {
    
    
				flag = 1;
			} else {
    
    
				if (opp[a] == 0) {
    
    
					opp[a] = b;
				} else {
    
    
					unite(opp[a], b);
				}
				if (opp[b] == 0) {
    
    
					opp[b] = a;
				} else {
    
    
					unite(opp[b], a);
				}
			}
		}
		printf("Scenario #%d:\n", idx);
		if (flag == 1) {
    
    
			puts("Suspicious bugs found!");
		} else {
    
    
			puts("No suspicious bugs found!");
		}
		puts("");
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43772166/article/details/108884362