bzoj 2654. tree(wqs 二分 + kruscal)

bzoj 2654. tree(wqs 二分 + kruscal)_第1张图片


设包含 x x x 条白边的最小生成树的权值为 g ( x ) g(x) g(x) ( x , g ( x ) ) (x,g(x)) (x,g(x)) 在二维平面上是一个上凸包的形式。

考虑让每条白边加上一个权值 x x x,显然 x x x 越大,最小生成树包含的白边越少, x x x 越小,最小生成树包含的白边越多。

考虑二分 x x x,设计算得到的答案为 f ( x ) f(x) f(x),此时选了 y y y 条白边,由于生成树可能不唯一,可能二分不到一个精确的答案 x x x 使得恰好用了 need 条白边,找到第一个 x x x 满足 y < n e e d y < need y<need x − 1 x - 1 x1 时的 y y y 一定 > = n e e d >= need >=need f ( x − 1 ) − n e e d ∗ ( x − 1 ) f(x - 1) - need * (x - 1) f(x1)need(x1) 就是答案。


代码:

#include
using namespace std;
const int maxn = 2e5 + 10;
struct node{
	int u,v,w,c;
	node() {};
	node(int ui,int vi,int wi,int ci) {
		u = ui;
		v = vi;
		w = wi;
		c = ci;
	}
	bool operator < (const node &rhs) const {
		if (w == rhs.w) return c < rhs.c;
		return w < rhs.w;
	}
}e[maxn],t[maxn];
int n,m,tot,cnt,p[maxn],sum;
int find(int x) {
	return p[x] == x ? x : p[x] = find(p[x]);
}
int solve(int x,int &ans) {
	ans = 0;
	for (int i = 1; i <= m; i++) {
		t[i] = e[i];
		if (e[i].c == 0)
			t[i].w += x;
	}
	sort(t + 1,t + m + 1);
	for (int i = 1; i <= n; i++)
		p[i] = i;
	int cnt = 0;
	for (int i = 1; i <= m; i++) {
		int u = t[i].u, v = t[i].v;
		int fu = find(u), fv = find(v);
		if (fu != fv) {
			if (t[i].c == 0) cnt++;
			p[fu] = fv; 
			ans += t[i].w;
		}
	}	
	return cnt;
}
int main() {
	scanf("%d%d%d",&n,&m,&tot);
	for (int i = 1; i <= m; i++) {
		int u,v,c,w; scanf("%d%d%d%d",&u,&v,&w,&c);
		u++; v++;
		e[i] = node(u,v,w,c);
		sum += w;;
	}
	int l = -sum, r = sum, ans = 0;
	while (l < r) {
		int mid = l + r >> 1;
		int cnt = solve(mid,ans);
		if (cnt < tot) r = mid;
		else l = mid + 1;
	}
	int cnt = solve(l - 1,ans);
	printf("%d\n",ans - tot * (l - 1));
	return 0;
}

你可能感兴趣的:(wqs二分,kruscal)