BZOJ4237稻草人——单调栈+CDQ分治

题目描述

JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。
有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:
田地的形状是边平行于坐标轴的长方形;
左下角和右上角各有一个稻草人;
田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数

输入

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

输出

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

样例输入

4
0 0
2 2
3 4
4 3

样例输出

3

提示 

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

 
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$分治,每次将区间内的点分为两部分,考虑右上角在上半部分,左下角在下半部分的贡献。对于上半部分维护单调递增的单调栈,对于下半部分维护单调递减的单调栈,这样对于上面每个点在下面的单调栈中二分即可得到贡献。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=998244353;
struct lty
{
	int x,y;
}q[200010];
int up[200010];
int t1,t2,n;
int down[200010];
ll ans;
bool cmp1(lty a,lty b)
{
	return a.y<b.y;
}
bool cmp2(lty a,lty b)
{
	return a.x<b.x;
}
int find(int val)
{
	int l=1,r=t2;
	if(t2==0)return 0;
	int res=-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(q[down[mid]].x>val)
		{
			r=mid-1;
			res=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	if(res==-1)return 0;
	return t2-res+1;
}
void CDQ(int l,int r)
{
	if(l==r)return ;
	int mid=(l+r)>>1;
	sort(q+l,q+r+1,cmp1);
	sort(q+l,q+mid+1,cmp2);
	sort(q+mid+1,q+r+1,cmp2);
	t1=t2=0;
	int num=l;
	for(int i=mid+1;i<=r;i++)
	{
		while(t1&&q[i].y<=q[up[t1]].y)
		{
			t1--;
		}
		up[++t1]=i;
		while(q[num].x<q[i].x&&num<=mid)
		{
			while(t2&&q[num].y>=q[down[t2]].y)
			{
				t2--;
			}
			down[++t2]=num;
			num++;
		}
		ans+=find(q[up[t1-1]].x);
	}
	CDQ(l,mid),CDQ(mid+1,r);
}
int main()
{
	scanf("%d",&n);
	q[0].x=q[0].y=-1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&q[i].x,&q[i].y);
	}
	CDQ(1,n);
	printf("%lld",ans);
}
posted @ 2019-06-05 21:30  The_Virtuoso  阅读(308)  评论(0编辑  收藏  举报