LuoguP2078 朋友 题解

Update

  • \(\texttt{2020.9.15}\) 重新排版了此题解,并删除了一些废话。

Content

有两个公司,\(A\) 公司里有 \(n\) 个员工,\(p\) 个朋友关系,员工编号为正整数,其中小明的编号为 \(1\)\(B\) 公司里有 \(m\) 个员工,\(q\) 个朋友关系,员工编号为负整数,其中小红的编号为 \(-1\)。试问通过小明和小红认识的人最多能够配成多少对情侣。

数据范围:\(n,m\leqslant10000,p,q\leqslant20000\)

Solution

一道经典的并查集题目。

一开始想到直接用个简单的查找合并就行了,可是一看,有男女之分要用两个数组!没事,用个数组指针轻松解决:

int find(int x, int *f) {
	return f[x] == x ? x : f[x] = find(f[x], f);
}
void unionn(int x, int y, int *f) {
	x = find(x, f), y = find(y, f);
	if(x != y)
		f[y] = x;
}

这样,男女分别开两个数组,然后使用指针去调用就能轻松搞定这个问题。

等等!女生的编号是负数,怎么办?取相反数,存入女生的数组就行。

然后查询这个很简单,遍历一下,看是否和(小明/小红)的父亲(直接拿小明和小红比较就错了!我就这样,所以一开始交只有10分)一样就行了:

//	puts("In man:");
	for(int i = 1; i <= n; ++i) {
//		printf("%d's father is %d\n", i, find(i, f1));
		if(find(i, f1) == find(1, f1))	ans1++;
	}
//	puts("In woman:");
	for(int i = 1; i <= m; ++i) {
//		printf("%d's father is %d\n", -i, find(-i, f2));
		if(find(-i, f2) == find(-1, f2))	ans2++;//注意这里也要取相反数!		
	}
	ans = min(ans1, ans2);

总体来说并查集的思路是很清楚的,不过需要点技巧。

Code

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;

int n, m, p, q, x, y, f1[100007], f2[100007], ans1, ans2, ans;

int find(int x, int *f) {
	return f[x] == x ? x : f[x] = find(f[x], f);
}
void unionn(int x, int y, int *f) {
	x = find(x, f), y = find(y, f);
	if(x != y)
		f[y] = x;
}

int main() {
	scanf("%d%d%d%d", &n, &m, &p, &q);
	for(int i = 1; i <= n; ++i)
		f1[i] = i;
	for(int i = 1; i <= m; ++i)
		f2[-i] = -i;
	for(int i = 1; i <= p; ++i) {
		scanf("%d%d", &x, &y);
		unionn(x, y, f1);
	}
	for(int i = 1; i <= q; ++i) {
		scanf("%d%d", &x, &y);
		unionn(x, y, f2);
	}
	for(int i = 1; i <= n; ++i) {
		if(find(i, f1) == find(1, f1))	ans1++;
	}
	for(int i = 1; i <= m; ++i) {
		if(find(-i, f2) == find(-1, f2))	ans2++;		
	}
	ans = min(ans1, ans2);
	printf("%d", ans);
	return 0;
}
posted @ 2021-12-23 21:03  Eason_AC  阅读(44)  评论(0)    收藏  举报