BZOJ4237稻草人——单调栈+CDQ分治
题目描述
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); }