[BZOJ4237]稻草人(CDQ分治)
先按y排序,二分,两边递归下去,然后处理下半部分对上半部分的贡献,即左下点在下半部分,右上点在上半部分的合法矩形个数。
两个部分均按x排序,枚举右上点p,则左下点需要满足:
1.横坐标大于上半部分纵坐标比p小的点的最大横坐标k。
2.不存在下半部分点满足纵坐标在两点之间,横坐标也在两点之间。
这样,我们对上半部分维护一个纵坐标单减的单调栈,下半部分维护纵坐标单增的单调栈。
每次枚举p时,将下半部分所有横坐标小于p的点加入栈中,再在栈中二分k即可。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=200010; 9 ll ans; 10 int n,stk1[N],stk2[N]; 11 struct P{ int x,y; }p[N]; 12 bool cmpx(const P &a,const P &b){ return a.x<b.x; } 13 bool cmpy(const P &a,const P &b){ return a.y<b.y; } 14 15 void solve(int L,int R){ 16 if (R<=L) return; 17 sort(p+L,p+R+1,cmpy); int mid=(L+R)>>1; 18 sort(p+L,p+mid+1,cmpx); sort(p+mid+1,p+R+1,cmpx); 19 int top1=0,top2=0,w=L; 20 rep(i,mid+1,R){ 21 while (top1 && p[stk1[top1]].y>p[i].y) top1--; 22 stk1[++top1]=i; 23 while (w<=mid && p[w].x<p[i].x){ 24 while (top2 && p[stk2[top2]].y<p[w].y) top2--; 25 stk2[++top2]=w; w++; 26 } 27 int l=1,r=top2,pos=-1; 28 while (l<=r){ 29 int m=(l+r)>>1; 30 if (p[stk2[m]].x>p[stk1[top1-1]].x) pos=m,r=m-1; else l=m+1; 31 } 32 if (~pos) ans+=top2-pos+1; 33 } 34 solve(L,mid); solve(mid+1,R); 35 } 36 37 int main(){ 38 freopen("bzoj4237.in","r",stdin); 39 freopen("bzoj4237.out","w",stdout); 40 scanf("%d",&n); 41 rep(i,1,n) scanf("%d%d",&p[i].x,&p[i].y); 42 p[0]=(P){-1,-1}; solve(1,n); printf("%lld\n",ans); 43 return 0; 44 }