题目描述
JOI村有一片荒地,上面竖着 N N N个稻草人,村民们每年多次在稻草人们的周围举行祭典。 有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件: 田地的形状是边平行于坐标轴的长方形; 左下角和右上角各有一个稻草人; 田地的内部(不包括边界)没有稻草人。 给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数。
输入格式
一行一个整数 N N N,代表稻草人的个数
接下来 N N N行,第 i i i行包含 2 2 2个由空格分隔的整数 x i x_i xi和 y i y_i yi,表示第 i i i个稻草人的坐标
输出格式
输出一行一个正整数,代表遵从启示的田地的个数
样例输入
4
0 0
2 2
3 4
4 3
样例输出
3
样例解释
所有满足题意的田地如图所示:
数据范围
-
1 ≤ N ≤ 2 × 1 0 5 1\leq N\leq 2\times 10^5 1≤N≤2×105
-
0 ≤ x i , y i ≤ 1 0 9 ( 1 ≤ i ≤ N ) 0\leq x_i,y_i\leq 10^9(1\leq i\leq N) 0≤xi,yi≤109(1≤i≤N)
-
x i x_i xi互不相同, y i y_i yi互不相同
题解
前置知识: c d q cdq cdq分治
题目大意就是在一个平面上有若干个点,求有多少个区域,满足区域左下角和右上角各有一个点,区域内没有点(不包括边界)。因为题目满足: ∀ i , j ( i ≠ j ) , x i ≠ x j , y i ≠ y j \forall i,j(i\neq j),x_i\neq x_j, y_i\neq y_j ∀i,j(i=j),xi=xj,yi=yj,所以只如果左下角和右上角有点,那区间的边界就不会有点。我们就不用特殊考虑边界,直接做就行了。
首先对 x x x值进行排序,然后 c d q cdq cdq分治。对于区间 [ l , r ] [l,r] [l,r],我们让左区间中的点作左下角的点,右区间的点做右上角的点。因为已经按 x x x值排好了序,所以左区间的 x x x值都小于右区间的 x x x值。分别对左右区间的 y y y值进行排序,左区间 x x x值仍小于右区间 x x x值。
对于左区间的点 i , j i,j i,j,如果 x i < x j x_i<x_j xi<xj且 y i < y j y_i<y_j yi<yj,因为剩余的右区间的点 y y y值都大于 y j y_j yj,所以此时点 i i i无法与剩下的任何一个右上角的点形成矩形,所以此时要将 i i i踢掉。我们可以用单调栈来维护。因为 y y y值已经从小到大排序,所以在栈中只需让 x x x值单调递减即可。
对于右区间的点 i , j i,j i,j,如果 x i > x j x_i>x_j xi>xj且 y i < y j y_i<y_j yi<yj,那么 j j j点对 i i i点不会产生任何影响。相反地,如果 x i < x j x_i<x_j xi<xj且 y i < y j y_i<y_j yi<yj,那点 i i i就不能与 x x x值小于 x j x_j xj 的左区间的点匹配。此时的 j j j对 i i i有限制作用,需保留。同样地,我们可以使用单调栈来维护,在栈中 x x x值单调递增。
对于每个右区间的点 i i i,设它在栈中前一个位置是点 j j j,那点 i i i的贡献就是左区间的栈中大于 x j x_j xj的点的个数,二分查找即可。总时间复杂度为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
code
#include<bits/stdc++.h>
using namespace std;
int n,t1,t2,h1[200005],h2[200005];
long long ans=0;
struct node{
int x,y;
}w[200005];
bool cmp1(node ax,node bx){
return ax.x<bx.x;
}
bool cmp2(node ax,node bx){
return ax.y<bx.y;
}
int find(int g){
int l=1,r=t1,mid;
while(l<=r){
mid=l+r>>1;
if(w[h1[mid]].y<g) l=mid+1;
else r=mid-1;
}
return l-1;
}
void cdq(int l,int r){
if(l==r) return;
int mid=l+r>>1;
cdq(l,mid);cdq(mid+1,r);
sort(w+l,w+mid+1,cmp2);
sort(w+mid+1,w+r+1,cmp2);
int j=l;t1=t2=0;
for(int i=mid+1;i<=r;i++){
while(w[j].y<w[i].y&&j<=mid){
while(t1&&w[h1[t1]].x<w[j].x) --t1;
h1[++t1]=j;++j;
}
while(t2&&w[h2[t2]].x>w[i].x) --t2;
h2[++t2]=i;
if(t2>1) ans+=t1-find(w[h2[t2-1]].y);
else ans+=t1;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&w[i].x,&w[i].y);
}
sort(w+1,w+n+1,cmp1);
cdq(1,n);
printf("%lld",ans);
return 0;
}