[BZOJ4237]稻草人

Posted by Dispwnl on March 15, 2019

题目

Description

JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。

有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:

田地的形状是边平行于坐标轴的长方形;

左下角和右上角各有一个稻草人;

田地的内部(不包括边界)没有稻草人。

给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数

Input

第一行一个正整数N,代表稻草人的个数

接下来N行,第i行(1<=i<=N)包含2个由空格分隔的整数Xi和Yi,表示第i个稻草人的坐标

Output

输出一行一个正整数,代表遵从启示的田地的个数

Sample Input

4
0 0
2 2
3 4
4 3

Sample Output

3

HINT

所有满足要求的田地由下图所示:

img

1<=N<=2*10^5

0<=Xi<=10^9(1<=i<=N)

0<=Yi<=10^9(1<=i<=N)

Xi(1<=i<=N)互不相同。

Yi(1<=i<=N)互不相同。

题解

考虑$CDQ$分治,考虑$CDQ$分治怎么统计左右之间的答案

对于左边的某个点$(x,y)$来说,横坐标大于它的左边的点纵坐标第一个大于它的为$y’$,则它能贡献的答案就是区间右区间中纵坐标在$[y,y’]$之间的点,并且纵坐标必须单调递减

左右区间分别按纵坐标从大到小排序,枚举左区间,左区间中每个点对应着右区间的一个前缀,可以用单调栈维护纵坐标大于$y$且单调递减的点,对于左区间的点,也可以用单调栈维护找到纵坐标第一个大于它的点,然后在右区间的单调栈上二分即可

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define mid (l+r>>1)
# define LL long long
using namespace std;
const int MAX=2e5+5;
struct p{
	int x,y;
	bool operator< (const p &a)
	const{
		return x<a.x;
	}
}A[MAX];
int n,Top,_Top;
LL ans;
int st[MAX],_st[MAX],_id[MAX],id[MAX];
int read()
{
	int x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
int look(int x)
{
	int l=1,r=Top,ans=Top+1;
	while(l<=r)
	{
		if(A[st[mid]].y<x) ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}
void Solve(int l=1,int r=n)
{
	if(l==r) return;
	Solve(l,mid),Solve(mid+1,r),Top=_Top=0;
	for(int i=l,L=mid+1;i<=mid;++i)
	  {
	  	for(;L<=r&&A[id[L]].y>A[id[i]].y;++L)
	  	  {
	  	  	while(Top&&A[id[L]].x<A[st[Top]].x) --Top;
	  	  	st[++Top]=id[L];
		  }
		while(_Top&&A[id[i]].x>A[_st[_Top]].x) --_Top;
		if(!_Top) ans+=Top;
		else ans+=Top-look(A[_st[_Top]].y)+1;
		_st[++_Top]=id[i];
	  }
	for(int i=l,L=l,R=mid+1;i<=r;++i)
	  if((R>r||A[id[L]].y>A[id[R]].y)&&L<=mid) _id[i]=id[L++];
	  else if((L>mid||A[id[R]].y>A[id[L]].y)&&R<=r) _id[i]=id[R++];
	for(int i=l;i<=r;++i)
	  id[i]=_id[i];
}
int main()
{
	n=read();
	for(int i=1;i<=n;++i)
	  A[i].x=read(),A[i].y=read(),id[i]=i;
	sort(A+1,A+1+n);
	return Solve(),printf("%lld\n",ans),0;
}