[CF9E] Interesting Graph and Apples - 构造,并查集
Description
给出 n 个点,m 条边,问是否能通过加一些边,使得 n 个点构成有且仅有 n 条边的单个环。如果能,输出 YES 以及加上的边的条数,并按字典序打印加上的边的两个端点。如果有多组解,输出字典序最小的一种方法。否则输出 NO 即可。
Solution
做法其实比较丰富,尽量去弄一个简单点不容易错的
先特判两种特殊情况:不需要加任何边(连通,所有点度数都是 2),没救(有点的度数大于 2,有自环)
剩下的情况一定可以构造出解
我们先枚举一个点 i,再枚举一个点 j,如果这两个点所在集合不连通,并且度数都小于 2,就连边
这样做一遍下来以后,一定可以串成一条链
显然不会出现度数大于 2 的点
对于每个 i,如果它和任何另一个 j 所在集合不连通,那么枚举到那个集合的链端时一定会连上一条边
最后,我们找到两个度数为 1 的点,连边,就完事了
(代码已经混乱不堪了)
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
if (n == 1 && m == 0)
{
cout << "YES
1
1 1" << endl;
return 0;
}
vector<int> fa(n + 2);
for (int i = 1; i <= n; i++)
fa[i] = i;
vector<int> d(n + 2);
function<int(int)> find = [&](int x) -> int {
return x == fa[x] ? x : fa[x] = find(fa[x]);
};
function<void(int, int)> merge = [&](int x, int y) -> void {
x = find(x);
y = find(y);
fa[x] = y;
};
int circle_flag = 0, self_circle = 0;
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
if (u == v)
self_circle = 1;
if (find(u) == find(v))
circle_flag = 1;
merge(u, v);
d[u]++;
d[v]++;
}
int min_deg = 1e9, max_deg = 0;
for (int i = 1; i <= n; i++)
{
min_deg = min(min_deg, d[i]);
max_deg = max(max_deg, d[i]);
}
if (max_deg > 2 || (self_circle && n > 1))
{
cout << "NO" << endl;
}
else if ((circle_flag && min_deg == 2 && max_deg == 2))
{
cout << "YES" << endl
<< 0 << endl;
}
else
{
vector<pair<int, int>> ans;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
if (j != i)
{
if (find(i) != find(j) && d[i] < 2 && d[j] < 2)
{
ans.push_back({min(i, j), max(i, j)});
d[i]++;
d[j]++;
merge(i, j);
}
}
}
vector<int> tmp;
for (int i = 1; i <= n; i++)
if (d[i] < 2)
tmp.push_back(i);
ans.push_back({tmp[0], tmp[1]});
int flag = 1;
if (tmp.size() == 2)
{
d[tmp[0]]++;
d[tmp[1]]++;
for (int i = 1; i <= n; i++)
if (d[i] != 2)
flag = 0;
}
else
{
flag = 0;
}
if (flag)
{
cout << "YES" << endl
<< n - m << endl;
for (auto [x, y] : ans)
cout << x << " " << y << endl;
}
else
{
cout << "NO" << endl;
}
}
}