题目链接:http://codeforces.com/group/NVaJtLaLjS/contest/238651/problem/D
题面:
题目大意:给你一张图,要求加p条边使他变成q个联通快。所加的边的边权最小。
加边规则是:如果两个顶点在同一个联通块上,边权为1000.
如果在不同的联通块上,边权为min(1e9,两个联通块边权和+1);
PS:可以是加原本已经存在的边。
思路:用并查集处理一下每个联通块,然后用优先队列维护每个联通块的边权和,贪心的相连,最后剩余的边随便找一个联通块的两个顶点连起来。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int maxn = 1e5+7;
int num[maxn],fa[maxn],vis[maxn] = {0};
ll sum[maxn];
vector<P> ans;
struct node{
ll value;
int id;
bool operator<(const node &r)const{
return value > r.value;
}
};
int finds(int u){
return fa[u] == u ? u:fa[u] = finds(fa[u]);
}
int main(){
int n,m,p,q;
scanf("%d%d%d%d",&n,&m,&p,&q);
for(int i = 1;i <= n; i++){
num[i] = 1,fa[i] = i,sum[i] = 0;
}
for(int i = 0;i < m; i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
int fx = finds(u);
int fy = finds(v);
sum[fx] += w;
if(fx != fy){
fa[fy] = fx;
num[fx] += num[fy];
sum[fx] += sum[fy];
}
}
int block = 0;
priority_queue<node> que;
for(int i = 1;i <= n; i++){
int res = finds(i);
if(!vis[res]){
que.push(node{sum[res],res});
block++;
vis[res] = 1;
}
}
block = block - q;
if(block < 0){
printf("NO");
return 0;
}
while(!que.empty()&&block--){
node k1 = que.top();
que.pop();
if(que.empty())break;
node k2 = que.top();
que.pop();
num[k1.id] += num[k2.id];
fa[k2.id] = k1.id;
que.push(node{k1.value+k2.value+min(1000000000ll,k1.value+k2.value+1),k1.id});
ans.push_back({k1.id,k2.id});
p--;
}
if(p < 0){
printf("NO");
return 0;
}
if(p){
int flag = -1,cnt = 0;
for(int i = 1;i <= n; i++){
int res = finds(i);
if(num[res] != 1){
flag = res;
break;
}
}
if(flag == -1){
printf("NO");
return 0;
}
int a[2];
for(int i = 1;i <= n; i++){
if(finds(i) == flag){
a[cnt++] = i;
}
if(cnt == 2)break;
}
while(p--){
ans.push_back({a[0],a[1]});
}
}
printf("YES\n");
for(int i = 0;i < ans.size(); i++){
printf("%d %d\n",ans[i].first,ans[i].second);
}
return 0;
}