大魔法师 题解
题目背景
在异彩纷呈的魔法王国,每个魔法师都掌握着各种各样的魔法,谁掌握的魔法多,谁的地位就高,所以人们为了更好的生活,每天都会不断学习新魔法。
这天,魔法王国最厉害的魔法师\(hsl\)决定拿着自己的钱去周游世界,不再学习新魔法了,但是他是一个有修养的人,决定将自己会的所有魔法教给王国中的乞丐。
题目描述
\(hsl\) 一共会 \(n\) 种魔法,编号由 \(1\) 到 \(n\) , 王国有 \(m\) 个乞丐,为了防止垄断现象,每个人的愿望清单上只能有 \(n\) 种魔法中的 \(2\) 种,并且每种魔法只能教给一个人,现在 \(hsl\) 已经拿到了 \(m\) 个乞丐的愿望清单,第 \(i\) 个人的清单上有 \(2\) 种魔法 \(x_i\),\(y_i\) 。
同时 \(hsl\) 可以安排乞丐的顺序,每轮到一个乞丐,只要他的愿望中有魔法还没有被传授给别人, \(hsl\) 就会教给他,如果一个乞丐轮到他的时候一个魔法也学不到,他就会抑郁。
我们知道 \(hsl\) 是个有修养的人,他想让抑郁的人尽量少,请你帮他安排这些乞丐的顺序,满足最后抑郁的乞丐数量尽量少,为了减少输出量,你只需要输出最小的抑郁人数即可。
输入格式
第一行两个数 \(n\) , \(m\)
接下来 \(m\) 行,每行两个数 \(x_i\) , $ y_i$
输出格式
输出 \(1\) 行 \(1\) 个数 \(ans\),表示最少的抑郁人数。
样例
输入#1
5 4
4 3
3 4
1 2
1 4
输出#1
1
输入#2
6 5
2 3
2 1
3 4
6 5
4 5
输出#2
0
提示说明
样例1解释
一种合法的安排方案是\(1\) \(4\) \(3\) \(2\),\(1\) 号学会魔法 \(3\) 和 \(4\) , \(4\) 号学会魔法 \(1\) , \(3\) 号学会魔法 \(2\),只有 \(2\) 号 \(1\) 个人没有魔法可学,所以答案为 \(1\) 。
数据范围
数据保证 \(1 ≤ x_i , y_i ≤ n\) 且 \(x_i != y_i\)
对于 \(30\)% 数据: \(1 ≤ m ≤ 10\)
另外 \(10\)% 数据: 所有的 \(x_i\) 都相同
另外 \(10\)% 数据: \(m = n-1\),且 \(x_i = i\),\(y_i = i+1\)
对于 \(100\)% 数据: \(2 ≤n ≤ 5*10^5\), \(1 ≤m ≤ 5*10^5\)
题解
求最小的抑郁人数,换个角度,也就是让能学到魔法的人尽量多,我们只要找到一种安排方案,使尽量多的人学到魔法,最终用总人数减去即可。
-
将 \(n\) 种魔法看作 \(n\) 个点,如果一个乞丐同时想学 \(x\) 和 \(y\) ,那么就将 \(x\) 和 \(y\) 连边。
-
最终图中会产生若干联通块,对于一个大小为 \(size\) 的联通块,最多能使 \(size-1\) 个人学到魔法,方案是第一个人选两种,后面每个人都与前面的人有一个重合的,就只会学一种了
-
将每个联通块的贡献相加得到 \(sum\),最终答案即为 \(m-sum\) ,因为数据范围5e5,所以用并查集实现即可。
Code
#include <cstdio>
const int maxn = 5e5+10;
int n, m, sum, fa[maxn], siz[maxn];
int read(int x = 0, bool f = 0, char ch = getchar()) {
for(;ch < '0' || ch > '9';ch = getchar()) f = ch == '-';
for(;ch >= '0' && ch <= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch&15);
return f ? -x : x;
}
int findrt(int x) {
return fa[x] == x ? x : fa[x]=findrt(fa[x]);
}
int main() {
freopen("1.in","r",stdin);
n = read(), m = read();
for(int i = 1;i <= n; ++i) fa[i] = i, siz[i] = 1;
for(int i = 1;i <= m; ++i) {
int x = read(), y = read();
x = findrt(x), y = findrt(y);
if(x != y) fa[x] = y, siz[y] += siz[x];
}
for(int i = 1;i <= n; ++i) {
if(findrt(i) == i) sum += siz[i]-1;
}
printf("%d\n", m-sum);
return 0;
}