其他分享
首页 > 其他分享> > 关于wqs二分(凸优化) 实数二分和整数二分的一些讨论

关于wqs二分(凸优化) 实数二分和整数二分的一些讨论

作者:互联网

https://loj.ac/problem/2478

以上面这题为例,这道题斜率是不递增的,并且都是整数。

实数二分很爽,但是效率不高,对于斜率都是整数的,我们可以采用整数二分,但是需要注意一点细节:

wqs二分,是找一个斜率,使得第k个成为最优点。

但是,因为斜率可能出现一段相同的情况,因此可能有一个斜率,使得一干点同时成为最优,我们要第k个被包含在其中,这样就没有办法恰好找到了。

此时,在dp的过程,应该保证权值和相同时,选的点数尽可能多,这样找到一个斜率ans使得最优点恰好\(>=k\),真正答案就是\(dp值+ans*k\)。

“权值和相同时,选的点数尽可能多”是必要的,因为假设k在一段斜率相同的中间,而dp出的点数总是在k的左边,这样就会取到下一个斜率,就不是最优了。

参考代码:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

#define V vector<int>
#define pb push_back
#define si size()

const int N = 3e5 + 5;

int n, k, x, y, z;
int fi[N * 2], nt[N * 2], to[N * 2], v[N * 2], tot;

void link(int x, int y, int z) {
	nt[++ tot] = fi[x], to[tot] = y, v[tot] = z, fi[x] = tot;
}

#define pii pair<ll, int>
#define fs first
#define se second

int fa[N];
pii f[N][3], g[N][3];

ll w;

pii mv(pii a, pii b) {
	if(a.fs == b.fs) return a.se > b.se ? a : b;
	return a.fs > b.fs ? a : b;
}

pii operator + (pii a, pii b) {
	return pii(a.fs + b.fs, a.se + b.se);
}

void dg(int x) {
	f[x][0] = w < 0 ? pii(0, 0) : pii(w, 1);
	f[x][1] = pii(w, 1);
	f[x][2] = pii(w, 1);
	
	for(int i = fi[x]; i; i = nt[i]) {
		int y = to[i], z = v[i];
		if(y == fa[x]) continue;
		fa[y] = x;
		dg(y);
		
		g[x][2] = f[x][2] + f[y][0];
		
		g[x][1] = f[x][1] + f[y][0];
		pii d = f[x][2] + f[y][1];
		d.se --; d.fs += z - w;
		g[x][1] = mv(g[x][1], d);
		
		g[x][0] = f[x][0] + f[y][0];
		d = f[x][1] + f[y][1];
		d.se --; d.fs += z - w;
		g[x][0] = mv(g[x][0], d);
		
		f[x][0] = mv(g[x][0], mv(g[x][1], g[x][2]));
		f[x][1] = mv(g[x][1], g[x][2]);
		f[x][2] = g[x][2];
	}
}

int count(ll _w) {
	w = _w;
	dg(1);
	return f[1][0].se;
}

int main() {
	scanf("%d %d", &n, &k); k ++;
	fo(i, 1, n - 1) {
		scanf("%d %d %d", &x, &y, &z);
		link(x, y, z); link(y, x, z);
	}
	ll ans = 0;
	for(ll l = -3e11, r = 3e11; l <= r; ) {
		ll m = l + r >> 1;
		if(count(m) >= k) ans = m, r = m - 1; else l = m + 1;
	}
	count(ans);
	pp("%lld\n", f[1][0].fs - k * ans);
}

标签:二分,pii,fs,斜率,实数,wqs,int,se,define
来源: https://www.cnblogs.com/coldchair/p/12609384.html