[bzoj 4237] 稻草人
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
0 0
2 2
3 4
4 3
Sample Output
3
HINT
所有满足要求的田地由下图所示:
![](http://www.lydsy.com/JudgeOnline/upload/201508/aa.jpg)
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分治+单调栈+二分搜索
将所有点按y排序,然后将其分成上下两半,考虑左下角的点在下半部分,右上角的点在上部分的矩形有几个
我们将这些点按x值从小到大做,遇到一个右边的点i时,我们需要知道第一个y值比它大的左边的点j是哪个,对此我们可以维护一个y值单调递减的栈,将点i加进来后不断弹出在它前面的y值比它小的点。同时我们要维护下半部分一个y值单调递增的栈(类似单调队列),然后统计上边的栈中有多少个点的x值介于x[j]~x[i]之间。当点i作为矩形的左下角时,这些点便可作为矩形的右上角
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct Node 7 { 8 int x,y; 9 }a[200001]; 10 int s1[200001],s2[200001]; 11 long long ans; 12 int n; 13 bool cmpy(Node a,Node b) 14 { 15 return (a.y<b.y); 16 } 17 bool cmpx(Node a,Node b) 18 { 19 return (a.x<b.x); 20 } 21 void cdq(int l,int r) 22 {int j,i,p,top1=0,top2=0; 23 //cout<<l<<' '<<r<<endl; 24 if (l==r) return; 25 int mid=(l+r)/2; 26 cdq(l,mid);cdq(mid+1,r); 27 sort(a+l,a+mid+1,cmpx); 28 sort(a+mid+1,a+r+1,cmpx); 29 j=l; 30 for (i=mid+1;i<=r;i++) 31 { 32 while (top1&&a[s1[top1]].y>=a[i].y) top1--; 33 s1[++top1]=i; 34 while (a[j].x<a[i].x&&j<=mid) 35 { 36 while (top2&&a[s2[top2]].y<=a[j].y) top2--; 37 s2[++top2]=j; 38 j++; 39 } 40 int ll=1,rr=top2,std=a[s1[top1-1]].x,pos=-1; 41 //cout<<ll<<' '<<rr<<endl; 42 while(ll<=rr){ 43 int mid=(ll+rr)/2; 44 if(a[s2[mid]].x>std) pos=mid,rr=mid-1; 45 else ll=mid+1; 46 } 47 //cout<<top2<<' '<<pos<<endl; 48 if (pos!=-1) ans+=top2-pos+1; 49 } 50 } 51 int main() 52 {int i; 53 cin>>n; 54 a[0].x=a[0].y=-1; 55 for (i=1;i<=n;i++) 56 { 57 scanf("%d%d",&a[i].x,&a[i].y); 58 } 59 sort(a+1,a+n+1,cmpy); 60 cdq(1,n); 61 cout<<ans; 62 }