CF125E MST Company
题意翻译
求一种特殊的最小生成树。给定一个有n个节点和m条边的图,找出一个生成树满足从根节点1直接连向其余节点的边要恰好是k条,在此条件下生成树的权值和最小。
输入输出样例
输入样例#1:
4 5 2
1 2 1
2 3 1
3 4 1
1 3 3
1 4 2
输出样例#1:
3
1 5 2
话说我一开始以为和免费道路那题一样直接上3遍\(kruskal\)但一直WA14
然后就去看了题解
发现正解是二分 + \(kruskal\)
具体思路就是每次给起点为1的边二分一个值
让所有的起点为1的边都减去这个值
因为\(kruskal\)要先按照边权排序
所以减去二分的值以后边的排名就会发生改变
然后判断是否能选到k条起点为1的边
然后注意判断无解的情况
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 200005 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , m , k ;
int Num1 , upp , f[M] , tot , Num , Ans[M] ;
struct E {
int from , to , dis , Id ;
} edge[M] ;
inline bool operator < (E a , E b) { return a.dis == b.dis ? a.from < b.from : a.dis < b.dis ; }
int find(int x) { if(f[x] != x) f[x] = find(f[x]) ; return f[x] ; }
inline int chk(int mid) {
for(int i = 1 ; i <= m ; i ++)
if(edge[i].from == 1)
edge[i].dis += mid ;
for(int i = 1 ; i <= n ; i ++) f[i] = i ;
tot = 0 , Num = 0 ;
sort(edge + 1 , edge + m + 1) ;
for(int i = 1 , u , v , x , y ; i <= m ; i ++) {
u = edge[i].from , v = edge[i].to ;
x = find(u) , y = find(v) ;
if(x == y) continue ;
f[y] = x ; ++tot ; if(u == 1) ++Num ;
if(tot == n - 1) break ;
}
for(int i = 1 ; i <= m ; i ++)
if(edge[i].from == 1)
edge[i].dis -= mid ;
return Num ;
}
inline bool query(int mid) {
for(int i = 1 ; i <= m ; i ++)
if(edge[i].from == 1)
edge[i].dis += mid ;
for(int i = 1 ; i <= n ; i ++) f[i] = i ;
tot = 0 , Num = 0 ;
sort(edge + 1 , edge + m + 1) ;
for(int i = 1 , u , v , x , y ; i <= m ; i ++) {
u = edge[i].from , v = edge[i].to ;
x = find(u) , y = find(v) ;
if(x == y) continue ;
if(u == 1 && Num == k) continue ;
f[y] = x ; if(u == 1) ++Num ;
Ans[++tot] = edge[i].Id ;
if(tot == n - 1) break ;
}
if(Num < k || tot != n - 1) return false ;
return true ;
}
int main() {
n = read() ; m = read() ; k = read() ;
for(int i = 1 ; i <= m ; i ++) {
edge[i].from = read() , edge[i].to = read() ;
edge[i].dis = read() ; edge[i].Id = i ;
if(edge[i].from > edge[i].to) swap(edge[i].from , edge[i].to) ;
if(edge[i].from == 1) ++Num1 ;
}
int l = -100001 , r = 100001 ;
while(l <= r) {
int mid = (l + r) >> 1 ;
if(chk(mid) >= k) l = mid + 1 , upp = mid ;
else r = mid - 1 ;
}
if(upp < -100001) { printf("-1\n") ; return 0 ; }
if(!query(upp)) { printf("-1\n") ; return 0 ; }
printf("%d\n",n - 1) ;
for(int i = 1 ; i < n ; i ++) printf("%d ",Ans[i]) ;
return 0 ;
}