1. 程式人生 > >[BZOJ4237]稻草人(CDQ分治)

[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 }