[Usaco2016 Feb] Load Balancing

平面上有N个点。现在要划两条分别平行于x轴y轴的直线,使得四个部分的点数的最大值趋于最小。
O(N²logN)解法请点击:lookas2001
这里有一个加强版——N≤1e5,我们就要用更高效的算法去解决。

首先依旧把所有坐标离散。

我们枚举一条分割线,至多枚举N次。(比如说枚举每一条horizontal line)那么就把这个平面分成上下两部分。上面n1个,下面n2个。(n1 + n2 = n)

一条vertical line,把上面分成两部分n1 = x1 + x2,同样地,下面也分成两部分n2 = x3 + x4。
那么此时贡献即为:max(x1, x2, x3, x4) = max((max(x1, x2)), max(x3, x4)) = max((max(x1, n1 - x1)), max(x3, n2 - x3))。
单独考虑一个函数max(x, nn - x),x为自变量,nn为定值,则此函数为单峰函数(单峰函数:通俗来说就是单调性只改变一次,如二次函数)。于是,贡献即转化为:两个单峰函数的max。同样,取max后依旧是单峰函数(画个图就可以证明)。并且,式子中所有的单峰函数,都是凹函数(先减后增)。
这里有一个加强版——N≤1e5,我们就要用更高效的算法去解决。

首先依旧把所有坐标离散。

对于单峰函数,使用三分法可以在logN的时间内找到其极值。

每次进行三分,对于一组已经确定的分割线,我们需要快速得到其贡献。考虑用两个权值BIT维护,一个维护horizontal line上面的部分,一个维护下面的,就可以在logN时间完成check。

总时间复杂度O(Nlog²N)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define N 100010
using namespace std;
inline char gc() {
	static char now[1<<16], *S, *T;
	if(S == T) {T = (S = now) + fread(now, 1, 1<<16, stdin); if(S == T) return EOF;}
	return *S++;
}
inline int read() {
	int x = 0, f = 1; char c = gc();
	while(c < '0' || c > '9') {if(c == '-') f = -1; c = gc();}
	while(c >= '0' && c <= '9') {x = x * 10 + c - 48; c = gc();}
	return x * f;
}
vector<int> pos[N];
int X[N], Y[N], bx[N], by[N], bit1[N], bit2[N];
int n, mx, my;
inline void add1(int p, int x) {for(; p <= my; p+= p & -p) bit1[p]+= x;}
inline void add2(int p, int x) {for(; p <= my; p+= p & -p) bit2[p]+= x;}
inline int query1(int p) {int ret = 0; for(; p; p-= p & -p) ret+= bit1[p]; return ret;}
inline int query2(int p) {int ret = 0; for(; p; p-= p & -p) ret+= bit2[p]; return ret;}
inline int check(int ver) {
	int n1, n2, n3, n4;
	n1 = query1(ver - 1); n2 = query1(my) - query1(ver - 1);
	n3 = query2(ver - 1); n4 = query2(my) - query2(ver - 1);
	return max(n1, max(n2, max(n3, n4)));
}
int main() {
	n = read();
	for(int i = 1; i <= n; ++i) X[i] = read(), Y[i] = read();
	memcpy(bx, X, sizeof(bx)); memcpy(by, Y, sizeof(by));
	sort(bx+1, bx+n+1); sort(by+1, by+n+1);
	mx = unique(bx+1, bx+n+1) - bx - 1; my = unique(by+1, by+n+1) - by - 1;
	for(int i = 1; i <= n; ++i) pos[lower_bound(bx+1, bx+mx+1, X[i]) - bx].push_back(lower_bound(by+1, by+my+1, Y[i]) - by);
	memset(bit1, 0, sizeof(bit1)); memset(bit2, 0, sizeof(bit2));
	for(int i = 1; i <= n; ++i) add2(lower_bound(by+1, by+my+1, Y[i]) - by, 1);
	int ans = n;
	for(int hor = 1; hor <= mx; ++hor) {
		for(vector<int>::iterator i = pos[hor - 1].begin(); i != pos[hor - 1].end(); ++i) {add1(*i, 1); add2(*i, -1);}
		int l = 1, r = my, lm, rm, flm, frm;
		while(l < r) {
			double len = (r - l)/3;
			lm = (int)l + len; rm = (int)lm + len;
			flm = check(lm); frm = check(rm);
			if(flm < frm) r = rm; else l = lm;
			if(l == r || !(int)len) {ans = min(min(flm, frm), ans); for(int i = l; i <= r; ++i) ans = min(ans, check(i)); break;}
		}
	}
	printf("%d", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/richard_for_oi/article/details/80257960