P6172 [USACO16FEB]Load Balancing P


Load Balancing P

将平面直角坐标系中的点用两条平行于 \(x/y\) 轴的直线分成 \(4\) 部分,求区域内最多奶牛数量的最小值。

二分答案,难点在判定上。

按照普通贪心思路,枚举 \(x\) 后枚举 \(y\),那显然是 \(O(n^2\log n)\) 的,跑到天荒地老。

所以需要使用数据结构优化这个过程可以考虑从小到大枚举 \(y\)\(x\) 也是一样的),然后在树状数组上二分。

但是这样是 \(O(n\log^3 n)\) 看上去很不优美,发现可以双指针一遍扫过去,于是就变成 \(O(n\log ^2 n)\) 了。

考虑实现将 \(x\) 离散化一下可以达到更加优美的复杂度哦。

#include
#include
#include
using namespace std;

const int N = 100010;
int n, m, b[N], up[N], down[N];
struct node{int x, y;} a[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool cmp(node a, node b){return a.y < b.y;}

void Modify(int c[], int x, int v){for(; x <= m; x += x & -x) c[x] += v;}
int Query(int c[], int x){int sum = 0; for(; x; x -= x & -x) sum += c[x]; return sum;}

bool chck(int mid){
	int Up = n, Down = 0, S = 1, T = n;
	for(int i = 1; i <= n; i ++) up[i] = down[i] = 0;
	for(int i = 1; i <= n; i ++) Modify(up, a[i].x, 1);
	for(int i = 1, j; i <= n; i = j){
		j = i;
		while(a[i].y == a[j].y){
			Modify(up, a[j].x, - 1);
			Modify(down, a[j].x, + 1);
			j ++, Up --, Down ++;
		}
		while(S <= n && Query(up, S) <= mid) S ++; S --;
		while(T >= 1 && Query(down, T) > mid) T --;
		int t = min(S, T);
		if(Up - Query(up, t) <= mid && Down - Query(down, t) <= mid)
			return true;
	}
	return false;
}

int main(){
	n = read();
	for(int i = 1; i <= n; i ++){
		a[i].x = read(), a[i].y = read();
		b[i] = a[i].x;
	}
	sort(b + 1, b + n + 1);
	m = unique(b + 1, b + n + 1) - (b + 1);
	for(int i = 1; i <= n; i ++)
		a[i].x = lower_bound(b + 1, b + m + 1, a[i].x) - b;
	sort(a + 1, a + n + 1, cmp);
	int l = 0, r = n;
	while(l < r){
		int mid = (l + r) >> 1;
		if(chck(mid)) r = mid;
		else l = mid + 1;
	}
	printf("%d\n", l);
	return 0;
}