Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
Sample Input
2 2 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
Solution
这做法反正我是想不到的...
考虑怎么让拎出来的白色边减少
我们可以给白色边加个权值
然后又因为这个权值有单调性,所以二分来求最小
二分之后拿最小生成树判定就好了
#include <bits/stdc++.h> using namespace std ; #define N 100010 #define inf 0x3f3f3f3f int n , m , sum = 0 , ans = 0 ; struct node { int u , v , w , col ; } e[ N ] ; int cnt , ned , t = 0 ; int f[ N ] ; bool cmp( node a , node b ) { return ( !a.col ? a.w + t : a.w ) < ( !b.col ? b.w + t : b.w ) ; } int find( int x ) { if( f[ x ] == x ) return x ; return f[ x ] = find( f[ x ] ) ; } bool check( int x ) { t = x ; for( int i = 1 ; i <= n ; i ++ ) f[ i ] = i ; sort( e + 1 , e + cnt + 1 , cmp ) ; int tot = 0 ; sum = 0 ; for( int i = 1 ; i <= cnt ; i ++ ) { int x = find( e[ i ].u ) , y = find( e[ i ].v ) ; if( x != y ) { f[ y ] = x ; sum += e[ i ].w ; if( !e[ i ].col ) tot ++ ; } } return tot >= ned ; } int main() { scanf( "%d%d%d" , &n , &m , &ned ) ; for( int i = 1 , u , v , w , col ; i <= m ; i ++ ) { scanf( "%d%d%d%d" , &u , &v , &w , &col ) ; e[ ++ cnt ].u = ++u ; e[ cnt ].v = ++v ; e[ cnt ].w = w ; e[ cnt ].col = col ; } int l = -1e5 , r = 1e5 ; while( l <= r ) { int mid = ( l + r ) >> 1 ; if( check( mid ) ) l = mid + 1 , ans = sum ; else r = mid - 1 ; } printf( "%d\n" , ans ) ; return 0 ; }