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;
}

浙公网安备 33010602011771号