题意
原题,此处提供题意简述。
有
n
(
1
≤
n
≤
2
×
1
0
4
)
n(1\leq n\leq 2\times10^4)
n(1≤n≤2×104) 个节点
m
(
1
≤
m
≤
1
0
5
)
m(1\leq m\leq 10^5)
m(1≤m≤105) 条双向边,每条边的边权都是
0
0
0 或
1
1
1。求能否构造出一个生成树使得该生成树中边权是
0
0
0 的边的数量一共有
k
k
k 条,若能输出方案,若不能输出 no solution
代码
转换题意,一共 n − 1 n-1 n−1 条边,需要有 k k k 条边权为 0 0 0 边,则有 n − k − 1 n-k-1 n−k−1 条边权是 1 1 1 的边。
我们首先按照边权从小到大连边,也就是优先连边权是 0 0 0 的边,用 u s e d used used 统计在该方案下所需最少的边权是 1 1 1 的边。这个步骤有 2 个目的:
- 确保运用 n − k − 1 n-k-1 n−k−1 条边权是 1 1 1 的边能够连接整张图。
- 确保除去这 u s e d used used 条边后剩下的图能够用边权是 0 0 0 的图联通。
如果
u
s
e
d
>
n
−
k
−
1
used > n-k-1
used>n−k−1,也就是必要加的边权是
1
1
1 的边条数大于规定,直接输出 no solution
。
接着,我们用部分边权是
1
1
1 的边替换边权是
0
0
0 的边,使得边权是
1
1
1 的边条数符合规定。如果找不到那么多边权是
1
1
1 的边,同样输出 no solution
。由于我们第一次跑克鲁斯卡尔算法的时候是优先加边权是
0
0
0 的边,这保证剩下的图能够用边权是
0
0
0 的图联通,因此部分替换后仍然可以用边权是
0
0
0 的边连接使得整张图联通成生成树。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,k;
struct edge{
int x,y,w;
bool nec = 0;//是否是必须要加入
}node[100005];
//从小到大排序
bool cmp(edge a,edge b) {
return a.w < b.w;
}
bool cmp1(edge a,edge b) {
return a.w > b.w;
}
int head[100005];
int find(int x) {
return head[x] == x?x:head[x] = find(head[x]);
}
int used = 0,lian = 0;
signed main() {
scanf("%lld %lld %lld",&n,&m,&k);
k = n - 1 - k;
for(int i = 1;i <= m;i++) {
scanf("%lld %lld %lld",&node[i].x,&node[i].y,&node[i].w);
}
sort(node + 1,node + m + 1,cmp);//从小到大排序
for(int i = 1;i <= n;i++) head[i] = i;
for(int i = 1;i <= m;i++) {
int p = find(node[i].x),q = find(node[i].y);
if(p != q) {
head[q] = p;
lian++;
if(node[i].w == 1) used++,node[i].nec = 1;
}
}
if(used > k or lian != n - 1) {
printf("no solution\n");
return 0;
}
for(int i = 1;i <= n;i++) head[i] = i;
sort(node + 1,node + m + 1,cmp1);
for(int i = 1;i <= m;i++) {
if(node[i].nec) {
head[find(node[i].y)] = find(node[i].x);
}
}
for(int i = 1;i <= m;i++) {
if(node[i].nec) continue;
int p = find(node[i].x),q = find(node[i].y);
if(used < k and node[i].w == 0) {
printf("no solution\n");
return 0;
}
if(used == k and node[i].w == 1) continue;
if(p == q) continue;
head[q] = p,node[i].nec = 1;
if(node[i].w == 1) used++;
}
for(int i = 1;i <= m;i++) {
if(node[i].nec == 1) printf("%lld %lld %lld\n",node[i].x,node[i].y,node[i].w);
}
return 0;
}