题目描述
JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。
有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:
田地的形状是边平行于坐标轴的长方形;
左下角和右上角各有一个稻草人;
田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数
输入
第一行一个正整数N,代表稻草人的个数
接下来N行,第i行(1<=i<=N)包含2个由空格分隔的整数Xi和Yi,表示第i个稻草人的坐标
输出
输出一行一个正整数,代表遵从启示的田地的个数
样例输入
4
0 0
2 2
3 4
4 3
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);
}