[bzoj4237]稻草人
1A了好高兴。。cdq分治的思想。感觉难点在单调栈。
Description
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数
Input
Output
Sample Input
4
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)$互不相同。
%出题人!!
首先分治处理:在区间$[l,r]$内,计算左下角的$x$在$[l,mid]$,右上角的$x$在$[mid+1,r]$的数量,递归下去。不重不漏。
那么就先将所有点按$x$排序,没啥说的。
那么在区间$[l,r]$内,分为左右两边$[l,mid],[mid+1,r]$:
左右各维护一个单调栈,左边称为$s1$,是单调递减,右边叫$s2$,是单调递增的。为啥呢?
考虑枚举右边的点。每次按$y$从小到大将右边的点加入$s2$。
右边的一个点$p$与左边的一个点$q$不能遵从启示(误)的情况有三种:
1.$p_y>q_y$ 2.$p,q$之间有左边的点 3.$p,q$之间有右边的点
于是我们知道对于左边的点,它左下方的点不会造成影响。类似的,对于右边的点,其右下方也不会造成影响。
所以按y从小到大将右边点顺序加入$s2$,同时将栈内比他的x大的弹出$s2$。因为这些点造成情况3时,当前点也可以造成。
然后将所有y小于当前点的左侧点加入$s1$。每加入一个点,将栈内比他的$x$小的弹出$s1$。因为这些点不可能再与右侧点形成矩形了——他们会被加入的这个点阻碍形成情况2。
于是每次查找比加入$s2$前,$s2$的栈顶的$y$大的,$s1$中的点有多少个。二分就行了。否则会出现情况3啊。
说的很麻烦,看代码吧qwq。跑得巨慢 =_=。下面我造的一组样例可以手玩一下。
#include<bits/stdc++.h> using namespace std; const int N=200010; inline int read(){ int r=0,c=getchar(); while(!isdigit(c))c=getchar(); while(isdigit(c)) r=r*10+c-'0',c=getchar(); return r; } struct qwq{ int x,y; }a[N]; bool cmpx(qwq p,qwq q){ return p.x<q.x; } bool cmpy(qwq p,qwq q){ return p.y<q.y; } struct Stack{ int t;qwq s[N]; Stack(){ t=0;memset(s,0,sizeof s); } int size(){return t;} bool empty(){return !t;} void pop(){t--;} void push(qwq x){s[++t]=x;} qwq top(){return s[t];} int find(int x){ int l=0,r=t; while(r-l>1){ int mid=l+r>>1; if(s[mid].y<x)l=mid; else r=mid; } if(s[r].y<x)return r; else return l; } void clear(){while(!empty())pop();} }sl,sr; long long ans; void cdq(int l,int r){ if(l==r)return; int mid=l+r>>1; sort(a+l,a+mid+1,cmpy);sort(a+mid+1,a+r+1,cmpy); sl.clear();sr.clear(); for(int i=mid+1,j=l;i<=r;i++){ while(!sr.empty()&&sr.top().x>a[i].x)sr.pop(); while(j<=mid&&a[j].y<a[i].y){ while(!sl.empty()&&sl.top().x<a[j].x)sl.pop(); sl.push(a[j]);j++; } int t=sl.size()-sl.find(sr.top().y); ans+=1ll*t; sr.push(a[i]); } sort(a+l,a+r+1,cmpx); cdq(l,mid);cdq(mid+1,r); } int main(){ int n=read(); for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(); sort(a+1,a+n+1,cmpx); cdq(1,n); printf("%lld\n",ans); } /* 7 3 1 7 2 4 3 1 4 6 5 2 6 5 7 */