F – Construct Highway
题目描述:
构造一颗树,包含输入的所有边,且每个顶点都具有指定度数d[i]
思路:
首先根据树的定义以及度的定义等可以发现如果
,则一定不可以构造成功 如果满足这个条件了以后,我们考虑贪心,由于有已经存在的边,所有n个点变成了若干个联通块,而且存在的边的两个端点的度数应该减1,显然,每个联通块所需要的度是所有点的度数和。
贪心:对于度数为1的联通块,肯定是连度数大于等于2的联通块才最好,所以我们可以根据度数是否为1进行分组,分成度数为1的组和度数大于等于2的组。对于一个度数大于等于2的联通块,假设他的度数为d,我们只需要拿d-1个度数为1的点去匹配就行,然后剩下的那一个度数,就放到度数为1的那组里面,用于别的度数大于等于2的组的匹配,这样是最优的
最终匹配完了以后必须是有两个度数为1的点,否则则不行。此外还要利用并查集来判断n个点是否连接成了一个树
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
typedef long long ll;
typedef pair <int,int> pii;
#define MAX 300000 + 50
int n, m;
int x, y;
int d[MAX];
int fa[MAX];
inline int getfa(int x){
return x == fa[x] ? x : fa[x] = getfa(fa[x]);
}
inline void emerge(int x, int y){
fa[getfa(x)] = getfa(y);
}
void work(){
cin >> n >> m;
for(int i = 1; i <= n; ++i)cin >> d[i];
for(int i = 1; i <= n; ++i)fa[i] = i;
for(int i = 1; i <= m; ++i){
cin >> x >> y;
--d[x];
--d[y];
emerge(x, y);
}
vector<vector<int>>v(n + 1);
for(int i = 1; i <= n; ++i){
if(d[i] < 0){
cout << -1 << endl;
return;
}
int ff = getfa(i);
for(int j = 1; j <= d[i]; ++j){
v[ff].push_back(i);
}
}
vector<int>ran;
vector<vector<int>>ranran;
for(int i = 1; i <= n; ++i){
if(v[i].size() == 1)ran.push_back(v[i][0]);
else if(v[i].size() > 1)ranran.push_back(v[i]);
}
vector<pii>ans;
for(auto u : ranran){
for(int i = 0; i < u.size() - 1; ++i){
if(ran.empty()){
cout << -1 << endl;
return;
}
emerge(u[i], ran.back());
ans.push_back(m_p(ran.back(), u[i]));
ran.pop_back();
}
ran.push_back(u.back());
}
if(ran.size() != 2){
cout << -1 << endl;
return;
}
emerge(ran[0], ran[1]);
ans.push_back(m_p(ran[0], ran[1]));
for(int i = 1; i <= n; ++i){
if(getfa(i) != getfa(1)){
cout << -1 << endl;
return;
}
}
for(auto [u, v] : ans){
cout << u << ' ' << v << endl;
}
}
int main(){
io;
work();
return 0;
}