刷题总结——稻草人(bzoj4237cdq分治)
题目:
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
所有满足要求的田地由下图所示:
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)互不相同。
题解:
首先每次分治前将点按y轴排序,消除y轴的影响,然后分为上下两部分,分别按x轴排序···
然后用两个单调栈分别维护上部分和下部分,上部分保证y单调递增,下部分保证y单调递减,这样每次将上部分的点作为右上角,找下部分符的点的数量
由于是按y轴排序且用了单调栈,我们已经排除了y轴的影响,只用找x轴符合情况的点,这时利用我们已经按x轴排的序,直接二分即可
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=2e5+5; struct node { int x,y; }p[N]; int n,stack1[N],top1,stack2[N],top2; long long ans=0; inline bool cmpy(node a,node b) { return a.y<b.y; } inline bool cmpx(node a,node b) { return a.x<b.x; } inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } inline void solve(int l,int r) { if(l==r) return; int mid=(l+r)/2;top1=top2=0; sort(p+l,p+r+1,cmpy); sort(p+l,p+mid+1,cmpx);sort(p+mid+1,p+r+1,cmpx); int j=l; for(int i=mid+1;i<=r;i++) { while(top1&&p[stack1[top1]].y>=p[i].y) top1--; stack1[++top1]=i; while(j<=mid&&p[j].x<p[i].x) { while(top2&&p[stack2[top2]].y<=p[j].y) top2--; stack2[++top2]=j; j++; } int st=p[stack1[top1-1]].x; int le=1,ri=top2,anss=-1; while(le<=ri) { int mi=(le+ri)/2; if(p[stack2[mi]].x>st) anss=mi,ri=mi-1; else le=mi+1; } if(anss!=-1) ans+=top2-anss+1; } solve(l,mid),solve(mid+1,r); } int main() { //freopen("a.in","r",stdin); n=R(); for(int i=1;i<=n;i++) p[i].x=R(),p[i].y=R(); p[0].x=-1,p[0].y=-1; solve(1,n); printf("%lld",ans); return 0; }
注意两个细节:
一是每次分治前都要按y轴排序
二是插入一个xy均为-1的点,不然后面二分时会出错