[国家集训队2]Tree I

题意

Here

思考

\(WQS\) 二分,第一次做,感觉细节有点多。

由于要求选 \(need\) 条白边,我们考虑每次给所有白边加上一个权值,再与黑边一起做生成树,这样就可以限制我们加入白边的个数了,但是这样我们还存在一个问题,如果有白边等于黑边权值,我们可能会先统计黑边,造成白边达不到 \(need\) 条的情况,我们只用在排序时加上第二关键字,优先选择白边即可。

代码


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x * f;
}
const int M = 100010;
const int N = 50050;
struct node{
	int from, to, dis, op, now;
}edge[M << 1];
bool cmp(node a, node b){
	return (a.now == b.now) ? a.op < b.op : a.now < b.now;
}
int fa[N];
int n, m, need, ans, sum, js;
int findx(int x){
	if(fa[x] == x) return x;
	return fa[x] = findx(fa[x]);
}
void mergex(int x, int y){
	int fx = findx(x), fy = findx(y);
	if(fx == fy) return;
	fa[fx] = fy;
}
bool check(int del){
	js = 0; sum = 0;
	for(int i=1; i<=n; i++) fa[i] = i;
	for(int i=1; i<=m; i++){
        edge[i].now = edge[i].dis;
        if(edge[i].op == 0) edge[i].now += del;
	}
	sort(edge+1, edge+m+1, cmp);
	for(int i=1; i<=m; i++){
		int u = edge[i].from, v = edge[i].to;
		int fu = findx(u), fv = findx(v);
		if(fu != fv){
            mergex(u, v); sum += edge[i].dis;
            if(edge[i].op == 0) js ++;
		}
	}
	return js >= need;
}
int main(){
	n = read(), m = read(), need = read();
	for(int i=1; i<=m; i++){
		edge[i].from = read(), edge[i].to = read(), edge[i].dis = read(), edge[i].op = read();
		edge[i].from ++, edge[i].to ++;
		if(edge[i].op == 1) edge[i].now = edge[i].dis;
	}
	int l = -100, r = 100;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(check(mid)){
			l = mid + 1;
			ans = sum;
		}
		else r = mid - 1;
	}
	cout<<ans;
	return 0;
}

总结

注意二分的判断条件是大于等于 \(need\) ,以及排序时注意第二关键字以避免无法出解。

posted @ 2018-11-05 13:46  alecli  阅读(157)  评论(0编辑  收藏  举报