BZOJ2654: tree 二分答案+最小生成树

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

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 ;
}

猜你喜欢

转载自www.cnblogs.com/henry-1202/p/BZOJ2654.html