抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题目传送门

题意

\(n\) 个点的完全图中去掉给定的 \(m\) 条边后,问是否存在一个生成树。若是,求出一种方案。

\(n,m\le 2\times 10^5\)

题解

显然贪心。假定以 \(1\) 为根节点,把 \(1\) 能连的点都与 \(1\) 相连,然后对这些点进行同样的操作,直到所有点都相互连通。

set 记录每个点不能连的点,以及当前还有哪些点没有加入最小生成树(记做 \(num\))。用队列记录已经加入的点,对于队列中的点 \(u\),遍历 \(num\),若可以连则连,加入队列,并且从 \(num\) 中删除;否则跳过。

关键是复杂度分析。对于遍历 \(num\) 时可以连的情况,这种情况一定不会超过 \(n-1\) 次,因为每次都会从 \(num\) 中删除一个点;对于不可以连的情况,一定不会超过 \(m\) 次,因为一条不能连的边不会重复遍历。所以总次数为 \(O(n+m)\) 次。由于判断一个点与另一个点能否连接需要 \(O(\log n)\),所以总复杂度为 \(O((n+m)\log n)\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <cstdio>
#include <cctype>
#include <set>
int read(){
register int x = 0;
register char f = 1, ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f ^= 1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return f ? x : -x;
}
#define N 200005
int n, m, h, t, Q[N], cnt, x[N], y[N];
std :: set<int> num, E[N];
int main(){
n = read(), m = read();
for (register int i = 1; i <= m; ++i){
int x = read(), y = read();
E[x].insert(y), E[y].insert(x);
}
for (register int i = 2; i <= n; ++i) num.insert(i);
h = 0, t = 1, Q[t] = 1;
while (h < t){
int u = Q[++h];
for (auto it = num.begin(), It = it; it != num.end(); ) // 遍历,注意 auto 需要 C++11
if (!E[u].count(*it)) x[++cnt] = u, y[cnt] = *it, Q[++t] = *it, It = it, ++it, num.erase(It);
else ++it;
}
if (num.size()) printf("No\n");
else{
printf("Yes\n");
for (register int i = 1; i <= cnt; ++i) printf("%d %d\n", x[i], y[i]);
}
}

评论