题目大意:给一个图,有黑边和白边,输出一种生成树方案使得白边恰有k条,可能无解。
没错,这是道水题。根据tutu的话,我们用LCT来维护。如果我们直接选白色,随便选k条,再全选黑色,可能使得图不连通。因为有些白色的边是必选的。
尽量选黑边,一遍生成树找出必选的白边。然后摧毁它,重建生成树。将必选的先放进去,如果不足k条,那就随便加够它,最后选黑边即可。
这样贪心,一开始我们选出了那些黑边不能代替的白边,然后剩下的所有白边一定能被黑边给替代了。我们加够k条,相当于将原来的树加成环再去掉一条黑边。所以最后一定能够构成一棵生成树,利用了生成树上强行加边去掉环上另一条边即可的性质。做生成树用并查集就行了。
无解的情况包括一开始基图不连通,必须要选的白边超过k条,或者选不够k条白边,或者强选k条白边后无法连通了(可能有多余的乱判=。=)。
代码:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#define maxn 20100
#define maxm 100100
using namespace std;
int n, m, k, Fa[maxn];
struct Data{
int x, y, z;
}E[maxm];
bool cmp1(Data A, Data B){
return A.z > B.z;
}
bool cmp2(Data A, Data B){
return A.z < B.z;
}
int FindR(int x){
if(Fa[x] == x) return x;
return Fa[x] = FindR(Fa[x]);
}
bool used[maxm];
int main(){
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= m; i++)
scanf("%d%d%d", &E[i].x, &E[i].y, &E[i].z);
sort(E+1, E+m+1, cmp1);
for(int i = 1; i <= n; i++) Fa[i] = i;
int cnt = 0;
for(int i = 1; i <= m; i++){
int r1 = FindR(E[i].x), r2 = FindR(E[i].y);
if(r1 != r2){
Fa[r1] = r2;
if(!E[i].z){
E[i].z = -1;
cnt ++;
}
}
}
if(cnt > k){
puts("no solution");
return 0;
}
Fa[1] = FindR(1);
for(int i = 2; i <= n; i++){
Fa[i] = FindR(i);
if(Fa[i] != Fa[i-1]){
puts("no solution");
return 0;
}
}
for(int i = 1; i <= n; i++) Fa[i] = i;
sort(E+1, E+m+1, cmp2);
for(int i = 1; i <= m; i++){
int r1 = FindR(E[i].x), r2 = FindR(E[i].y);
if(r1 != r2){
if(E[i].z == 1 || k){
used[i] = true;
Fa[r1] = r2;
if(E[i].z != 1){
k --;
E[i].z = 0;
}
}
}
}
if(k){
puts("no solution");
return 0;
}
Fa[1] = FindR(1);
for(int i = 2; i <= n; i++){
Fa[i] = FindR(i);
if(Fa[i] != Fa[i-1]){
puts("no solution");
return 0;
}
}
for(int i = 1; i <= m; i++)
if(used[i])
printf("%d %d %d\n", E[i].x, E[i].y, E[i].z);
return 0;
}