关于此题 [ABC239F] Construct Highway 的一些总结
-
题目大意:给定n个点和m条已有的边,并指定每个点的度数,问能否构建出满足条件的树
-
思路:首先所有度数之和应该是
(因为每条边会被算两次)。对于输入的每条边,我们让其对应所需的度数减一,得到的就是我们所构建出的树中,每个点需要我们人为额外加上的度数。此时如果有某个点所需度数为负就直接输出-1。那么此时,我们可以得到每个连通块所需要的总度数,我们根据每个连通块的需要的总度数,将这些连通块分为两类:第一类,所需总度数为1;第二类,所需总度数大于等于2。此时我们要进行的操作是,枚举每个第二类连通块,将其需要度数的点与第一类连通块中需要度数的点连边。假设此第二类连通块所需总度数 ,那么我们连上 条边,最后剩一个点还需连接一条边时,此第二类连通块就变成了第一类连通块,于是将其加入第一类连通块当中。在此过程中如果发现第一类连通块不够了,就直接输出-1。最后再让第一类连通块之间相互匹配,即可。注意此处用连通块所需总度数来分类而不是各个点的度数来分类,是因为如果按照后者情况我们有可能连上与自己在同一连通块内的点形成环,而想要避免这种情况又会花费 的复杂度。
代码:
#include<bits/stdc++.h>
using namespace std;
int t;
const int N = 2e5 + 10;
int fat[N],n,m,in[N],tot;
int cnt[N];
vector<int> q1;
vector<vector<int> > q2;
vector<int> nde[N];
struct node {
int x,y;
}ans[N];
int find(int x) {
return (fat[x] == x ? fat[x] : fat[x] = find(fat[x]));
}
void unionn(int x,int y) {
x = find(x);
y = find(y);
fat[x] = y;
}
bool check() {
int fg = find(1);
for(int i = 2;i <= n;i++) if(fg != find(i)) return true;
return false;
}
void solve() {
cin >> n >> m;
for(int i = 1;i <= n;i++) cin >> in[i],fat[i] = i;
if(accumulate(in + 1,in + 1 + n,0) != 2 * n - 2) return (void)puts("-1");
for(int u,v,i = 1;i <= m;i++) {
cin >> u >> v;
in[u]--;in[v]--;
unionn(u,v);
if(in[u] < 0 || in[v] < 0) return (void)puts("-1");
}
for(int i = 1;i <= n;i++)
for(int j = 1;j <= in[i];j++)
nde[find(i)].emplace_back(i);
for(int i = 1;i <= n;i++)
if(nde[i].size() == 1) q1.emplace_back(nde[i][0]);
else if(nde[i].size() > 1) q2.emplace_back(nde[i]);
for(auto&& i : q2) {
for(int j = 1;j < i.size();j++) {
if(q1.empty()) return (void)puts("-1");
ans[++tot] = {i[j],q1.back()};
unionn(i[j],q1.back());
q1.pop_back();
}
q1.push_back(i[0]);
}
if(q1.size() & 1) return (void)puts("-1");
for(int i = 0;i < q1.size();i += 2) {
ans[++tot] = {q1[i],q1[i + 1]};
unionn(q1[i],q1[i + 1]);
}
if(check()) return (void)puts("-1");
for(int i = 1;i <= tot;i++) cout << ans[i].x << ' ' << ans[i].y << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
t = 1;
while(t--) solve();
return 0;
}