题目链接:[USACO16FEB]负载平衡Load Balancing_Silver


题目大意:给你一个矩阵,里面有些点,让你横向切一刀,纵向切一刀,使得得到的四个区域内的最大的点数最少。

这道题目数据比较水,点的数量是1e3,于是我们可以用前缀和暴力枚举。

但是太没技术含量了,于是我们采用了一种 nlogn * logn 的做法。

我们把点离散化之后,枚举x坐标,然后用两个线段树或者树状数组维护x左右点的y坐标信息。然后二分y坐标即可。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
#define lowbit(x) (x&(-x))
using namespace std;
const int N=2e3+10;
int n,d[2][N],a[N],b[N],m,res=0x3f3f3f3f,tot0,tot1;
vector<int> x[N],v;
inline int id(int x){
	return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
inline void add(int k,int x,int v){
	for(;x<=m;x+=lowbit(x))	d[k][x]+=v;
}
inline int ask(int k,int x){
	int res=0; for(;x;x-=lowbit(x)) res+=d[k][x]; return res;
}
inline int check(int mid){
	int cnt0=ask(0,mid); int cnt1=ask(1,mid);
	return (max(cnt0,cnt1)>=max(tot0-cnt0,tot1-cnt1));
}
inline int bsearch(){
	int l=0,r=m; tot0=ask(0,m); tot1=ask(1,m);
	while(l<r){
		int mid=l+r>>1;
		if(check(mid))	r=mid;
		else	l=mid+1;
	}
	int cnt0=ask(0,l);	int cnt1=ask(1,l);
	return max(max(cnt0,cnt1),max(tot0-cnt0,tot1-cnt1));
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i]>>b[i];	v.push_back(a[i]); v.push_back(b[i]);
	}
	sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end());
	m=v.size();
	for(int i=1;i<=n;i++)	x[id(a[i])].push_back(id(b[i]));
	for(int i=1;i<=n;i++)	add(1,id(b[i]),1);
	for(int i=0;i<=m;i++){
		for(int j=0;j<x[i].size();j++)	add(0,x[i][j],1),add(1,x[i][j],-1);
		res=min(res,bsearch());
	}
	cout<<res<<endl;
	return 0;
}