=== ===
这里放传送门
=== ===
题解
显然题目中要求的是一棵包含k条鹅卵石路的生成树。首先把所有水泥道路都加入生成树里面去,看看有哪些鹅卵石路是不加进去就出不来生成树的。然后初始化并查集,先把那些必须加的鹅卵石路加进去,这样就保证了一定可以构造出生成树。然后剩下的就随便选就可以了,先选够k条鹅卵石路,再随便选几条水泥路搞出一棵树来就好啦。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,k,father[20010],d,root,cnt;
struct edge{
int k,u,v;
bool must,chs;
bool operator < (const edge &x)const{
return k>x.k;
}
}e[100010];
int find(int x){
if (father[x]!=x) father[x]=find(father[x]);
return father[x];
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++) father[i]=i;
for (int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].k);
sort(e+1,e+m+1);
for (int i=1;i<=m;i++){
int r1,r2;
if (e[i].k==0){d=i;break;}
r1=find(e[i].u);r2=find(e[i].v);
if (r1!=r2) father[r2]=r1;
}
for (int i=d;i<=m;i++){
int r1,r2;
r1=find(e[i].u);r2=find(e[i].v);
if (r1!=r2){
father[r2]=r1;
e[i].must=true;
}//判断哪些特殊边是必须加的
}
root=find(1);
for (int i=2;i<=n;i++)
if (find(i)!=root||m-d+1<k){
printf("no solution\n");
return 0;
}
for (int i=1;i<=n;i++) father[i]=i;
for (int i=d;i<=m;i++)
if (e[i].must==true){
int r1,r2;
r1=find(e[i].u);r2=find(e[i].v);
if (r1!=r2){
father[r2]=r1;++cnt;
e[i].chs=true;//先把必须加的加进去,打使用标记
}
}
if (cnt>k){
printf("no solution\n");
return 0;
}
for (int i=d;i<=m;i++){
int r1,r2;
if (cnt==k) break;
if (e[i].must==false){
int r1,r2;//补全k条特殊边
r1=find(e[i].u);r2=find(e[i].v);
if (r1!=r2){
father[r2]=r1;++cnt;
e[i].chs=true;
}
}
}
if (cnt<k){
printf("no solution\n");
return 0;
}
for (int i=1;i<d;i++){
int r1,r2;//补全生成树
if (cnt==n-1) break;
r1=find(e[i].u);r2=find(e[i].v);
if (r1!=r2){
father[r2]=r1;++cnt;
e[i].chs=true;
}
}
for (int i=1;i<=m;i++)
if (e[i].chs==true)
printf("%d %d %d\n",e[i].u,e[i].v,e[i].k);
return 0;
}