[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
",ans);
43     return 0;
44 }
原文地址:https://www.cnblogs.com/HocRiser/p/9894286.html