传送门:Load Balancing_Silver

传送门的题面虽然是Silver,但和Platinum的区别也只在数据范围,所以看题面看水谷就行了。Platinum的数据范围是1E5,此解法也可AC

看了一眼题,先敲了60行splay,再认真看题……卧槽根本不用平衡树。但是既然都写了……不用就太吃亏了。这里就写下平衡树解法吧。

首先标准的二分,看不出来请自裁。令x线指与y轴平行的线,y线指与x轴平行的线。我们发现不能二分套二分的来求x线和y线,因为这两个在一起并不满足单调性。

看一下数据范围,如果是$Nlog(N)$或者$Nlog^2(N)$应该还是能够接受的。考虑枚举x线,二分y线,这时候我们需要的就是两棵平衡树,L存着在x线左侧的所有点的y坐标,另一棵R存着在x线右侧的所有点的y坐标。每次x线向后移时,只需要在L中插入一些,在R中删除一些即可。

至于为什么二分y线就是满足单调的呢?这点稍微画下图就能明白。check的条件也就是

if max(左下,右下)<=max(左上,右上)
   then l=mid+1
   else r=mid

然后稍微注意一下代码细节。如果直接二分x线权值我们不好让mid全为偶数。注意到题目并没有让我们输出x线y线,我们可以改为二分最后一个小于等于x线权值的点的横坐标。

AC代码:

#include <bits/stdc++.h>
using namespace std;

inline int red(){
	int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
	for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 1e5+10;
const int INF = 0x3f3f3f3f;

int n,res=INF,ans=INF;
int ypos[N];

struct point{int x,y;}p[N];
bool cmp(point a,point b){return a.x<b.x;}

namespace Splay{
	struct S{
		int ch[N][2],fa[N],sze[N],cnt[N],val[N];
		int root,tot;
		#define ls(A) ch[A][0]
		#define rs(A) ch[A][1]
		void pushup(int x){sze[x]=sze[ls(x)]+sze[rs(x)]+cnt[x];}
		void conect(int a,int p,int b){ch[a][p]=b,fa[b]=a;}
		void rotate(int x){
			int y=fa[x],z=fa[y],k=(rs(y)==x),w=(rs(z)==y);
			conect(z,w,x),conect(y,k,ch[x][k^1]),conect(x,k^1,y);
			pushup(y),pushup(x);
		}
		void splay(int x,int s){
			while(fa[x]!=s){
				int y=fa[x],z=fa[y],k=(ls(y)==x),w=(ls(z)==y);
				if(z!=s)	rotate(k==w?y:x);
				rotate(x);
			}
			if(!s)	root=x;
		}
		void insert(int tar){
			int x=root,y=0;
			while(x&&val[x]!=tar)	y=x,x=ch[x][tar>val[x]];
			if(x)	{++cnt[x],splay(x,0);return;}
			x=++tot;
			if(y)	ch[y][tar>val[y]]=x;
			fa[x]=y,sze[x]=cnt[x]=1,val[x]=tar;
			splay(x,0);
		}
		void find(int tar){		
			int x=root;if(!root)	return;
			while(ch[x][tar>val[x]]&&val[x]!=tar)	x=ch[x][tar>val[x]];
			splay(x,0);
		}
		int drive(int tar,int f){
			find(tar);int x=root;
			if(val[x]>tar&&f)	return x;
			if(val[x]<tar&&!f)	return x;
			x=ch[x][f];
			while(ch[x][f^1])	x=ch[x][f^1];
			return x;
		}
		void dereta(int tar){
			int per=drive(tar,0),net=drive(tar,1);
			splay(per,0),splay(net,per);	
			int del=ls(net);
			if(cnt[del]>1)	--cnt[del],splay(del,0);
			else	ls(net)=0;
		}
		int num(int tar){
			find(tar);
			return sze[ls(root)]+cnt[root];
		}
		int sum(){return sze[ls(root)]+sze[rs(root)]+cnt[root];}
	}s[2];
}

//ld=left down lu=left up rd=right down ru=right up
int check(int mid,int x){
	int per=Splay::s[0].num(ypos[mid])-1,net=Splay::s[1].num(ypos[mid])-1;
	int lef=Splay::s[0].sum()-2,rig=Splay::s[1].sum()-2;
	int ld=per,lu=lef-ld,rd=net,ru=rig-rd;
	if(max(ld,rd)<=max(lu,ru))	return res=min(res,max(lu,ru)),1;
	return 0;
}

int main(){
	n=red();
	for(int i=1;i<=n;++i)
		p[i].x=red(),p[i].y=ypos[i]=red();
	sort(p+1,p+n+1,cmp);sort(ypos+1,ypos+n+1);
	Splay::s[0].insert(-1e9),Splay::s[0].insert(1e9);
	Splay::s[1].insert(-1e9),Splay::s[1].insert(1e9);
	for(int i=1;i<=n;++i)	Splay::s[1].insert(p[i].y);
	for(int i=1,cnt=1;i<=n;++i){
		while(p[cnt].x<=p[i].x&&cnt<=n)	Splay::s[0].insert(p[cnt].y),Splay::s[1].dereta(p[cnt++].y);
		int l=1,r=n;res=INF;
		while(l+1<=r){
			int mid=l+r>>1;
			if(check(mid,cnt-1))	l=mid+1;
			else					r=mid;
		}
		ans=min(ans,res);
	}
	printf("%d",ans);
	return 0;
}