BZOJ 2654: tree( 二分 + MST )
我们给白色的边增加权值 , 则选到的白色边就会变多 , 因此可以二分一下.
不过这道题有点小坑...
-------------------------------------------------------------------------------------------
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define rep( i , n ) for( int i = 0 ; i < n ; ++i )
#define clr( x , c ) memset( x , c , sizeof( x ) )
using namespace std;
const int maxn = 50000 + 5;
const int maxm = 100000 + 5;
int add , n , m , need;
int p[ maxn ];
int find( int x ) {
return p[ x ] == x ? x : p[ x ] = find( p[ x ] );
}
void UF_clear() {
rep( i , n ) p[ i ] = i;
}
struct edge {
int u , v , d , s;
void Read() {
scanf( "%d%d%d%d" , &u , &v , &d , &s );
s ^= 1;
}
bool operator < ( const edge &e ) const {
return d < e.d || ( d == e.d && s > e.s );
}
};
edge E[ maxm ];
struct node {
int L , R , w;
};
int kruskal() {
int ans = 0 , cnt = 0;
UF_clear();
rep( i , m ) {
edge &e = E[ i ];
if( e.s ) e.d += add;
}
sort( E , E + m );
rep( i , m ) {
edge &e = E[ i ];
int a = find( e.u ) , b = find( e.v );
if( a != b ) {
p[ a ] = b;
ans += e.d;
if( e.s ) cnt++;
}
}
rep( i , m ) {
edge &e = E[ i ];
if( e.s ) e.d -= add;
}
return cnt >= need ? ans : -1;
}
int main() {
freopen( "test.in" , "r" , stdin );
cin >> n >> m >> need;
rep( i , m )
E[ i ].Read();
int ans;
int L = -300 , R = 300;
while( L <= R ) {
add = ( L + R ) >> 1;
int x = kruskal();
if( x != -1 ) {
ans = x - need * add;
L = add + 1;
} else
R = add - 1;
}
cout << ans << "\n";
return 0;
}
-------------------------------------------------------------------------------------------
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 578 Solved: 214
[Submit][Status][Discuss]
Description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
Input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行
每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求生成树的边权和。
Sample Input
2 2 1
0 1 1 1
0 1 2 0
0 1 1 1
0 1 2 0
Sample Output
2
HINT
数据规模和约定
0:V<=10
1,2,3:V<=15
0,..,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。
Source