大魔法师 题解

题目背景

在异彩纷呈的魔法王国,每个魔法师都掌握着各种各样的魔法,谁掌握的魔法多,谁的地位就高,所以人们为了更好的生活,每天都会不断学习新魔法。

这天,魔法王国最厉害的魔法师\(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;
}
posted @ 2020-07-25 21:37  liuzhaoxu  阅读(122)  评论(0编辑  收藏  举报