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 ;
}
posted @ 2018-09-25 19:58  beretty  阅读(148)  评论(0编辑  收藏  举报