bzoj 4411 USACO16FEB 负载平衡Load Balancing(扫描线,树状数组,三分答案,前缀和)

题意:

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

思路:

首先考虑对于n大小在1000左右,直接二维前缀和预处理,枚举后,O(1)计算答案

对于n=100000,二维开不下,考虑加入数据结构维护区间和

于是加入树状数组,考虑直接枚举竖着的直线的横坐标,每次枚举时不断向树状数组加点(点的纵坐标)

这样对于外层循环的每一个i,当前的树状数组维护的都是横坐标 1~i 时,y方向上的点的区间个数和

下面举个例子

假设矩阵被分成这样      A3  A4

       A1  A2

那么A1中点的个数即为树状数组中sum(y),

为了求出A3,A2,一开始时维护x和y方向的一维前缀和

那么 A3=sum[x]-A1  A2=sum[y]-A1

A4=n-A1-A2-A3

这样的话就能把四个部分各自的点的数量表示出来

那么现在问题来了

我们不可能直接for来枚举横着的直线的纵坐标

又发现

min{max(A1,A2,A3,A4)}的值是个类似a<0的抛物线图像

满足上凸性质

所以可以用三分法对横坐标处理

三分判断条件见程序

最后横着的直线的y坐标在l处

直接计算答案


稍有问题,不过能过官方数据的WA代码

#include<bits/stdc++.h>
using namespace std;
const int N=101000;
struct data{
	int x,y;
}p[N];
int n,temp[N*2],cnt=0;
int id[10*N];
int maxy=-1,maxx=-1,minx=1e9,miny=1e9;
int ans=1e9;
int e[N],sumy[2*N],sumx[2*N];

bool comp(data a,data b)
{return a.x<b.x;}

int lowbit(int x){return x&(-x);}

void add(int x)
{
	while(x<N*2)
	{
		e[x]++;
		x+=lowbit(x);
	}
}

int sum(int x)
{
	int res=0;
	while(x>0)
	{
		res+=e[x];
		x-=lowbit(x);
	}
	return res;
}

void get_4(int *a,int x,int y)
{
	a[1]=sum(y);
	a[2]=sumy[y]-a[1];
	a[3]=sumx[x]-a[1];
	a[4]=n-a[1]-a[2]-a[3];
	sort(a+1,a+5);
}

void pre()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>p[i].x>>p[i].y;
		temp[++cnt]=p[i].x;
		temp[++cnt]=p[i].y;
		maxy=max(maxy,p[i].y);
		maxx=max(maxx,p[i].x);
		minx=min(minx,p[i].x);
		miny=min(miny,p[i].y);
	}
	sort(temp+1,temp+cnt+1);
	cnt=unique(temp+1,temp+cnt+1)-temp-1;
	for(int i=1;i<=cnt;i++)id[temp[i]]=i;
	sort(p+1,p+n+1,comp);
	
	for(int i=1;i<=n;i++) sumy[id[p[i].y]]++;
	for(int i=1;i<=cnt;i++) sumy[i]+=sumy[i-1];
	
	for(int i=1;i<=n;i++) sumx[id[p[i].x]]++;
	for(int i=1;i<=cnt;i++) sumx[i]+=sumx[i-1];
}
bool xiaoyu(int *a1,int *a2)
{
	for(int i=4;i>=1;i--)
	{
		if(a1[i]>a2[i])return 0;
		if(a1[i]<a2[i])return 1;
	}
}
void solve()
{
	int now=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=now;j<=n;j++)
			if(p[j].x<=p[i].x)add(id[p[j].y]);
			else{now=j;break;}
		int l=id[miny],r=id[maxy];
		int a1[5],a2[5],m1,m2;
		while(l<r)
		{
			m1=l+(r-l)/3;
			m2=r-(r-l)/3;
			get_4(a1,id[p[i].x],m1);
			get_4(a2,id[p[i].x],m2);
			
			if(xiaoyu(a1,a2))
				r=m2-1;
			else l=m1+1;
		}
		get_4(a1,id[p[i].x],l);
		ans=min(a1[4],ans);
	}
	cout<<ans;
}
int main()
{
//	freopen("in.in","r",stdin);
	pre();
	solve();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值