1. 程式人生 > >[ CDQ分治 ] BZOJ4237

[ CDQ分治 ] BZOJ4237

先將座標按 x 排序,然後考慮左端點在 [l,mid],右端點在 [mid+1,r] 的矩形個數。
將兩個區間分別按 y 降序排序,對於左區間維護一個 x 遞減的單調棧,對於右區間維護一個 x 遞增的單調棧。
列舉左區間的點 i,將右區間 y 座標大於當前點 y 座標的點加入。以 i 點為左端點的矩形個數就是右區間單調棧中 yyiyi1 內的矩形個數。
考慮為什麼可以這樣做。
對於右區間中的點 i,jj<i ,如果 xi<xj ,那麼 i 將在點 j 的左下角,於是將 j 刪除。
對於左區間中的 i,j,j<i ,如果 xi>xj ,那麼 xj

i 無影響,可以刪去。

#include<bits/stdc++.h>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'
&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc()); } #define N 200010 #define ll long long #define ab pair<int,int> #define x first #define y second ab a[N],b[N],c[N],t[N]; int i,j,k,n,m,p,l1,l2,p1,p2; ll Ans; inline void Add(ab x){ while(l2&&c[l2].x>x.x)l2--; c[++l2]=x; } inline
int Get(int x){ int l=1,r=l2; while(l<=r){ int Mid=l+r>>1; if(c[Mid].y>x)l=Mid+1;else r=Mid-1; } return r; } inline void Solve(int l,int r){ if(l==r)return; int Mid=l+r>>1; Solve(l,Mid);Solve(Mid+1,r); l1=l2=0; int j=Mid; for(int i=l;i<=Mid;i++){ while(j<r&&a[j+1].y>a[i].y)Add(a[++j]); while(l1&&b[l1].x<a[i].x)l1--; Ans+=l2-(l1?Get(b[l1].y):0);b[++l1]=a[i]; } int p1=l,p2=Mid+1,p=l; while(p1<=Mid||p2<=r) if(p1<=Mid&&(p2>r||a[p1].y>a[p2].y))t[p++]=a[p1++];else t[p++]=a[p2++]; for(int i=l;i<=r;i++)a[i]=t[i]; } int main(){ Read(n); for(i=1;i<=n;i++)Read(a[i].x),Read(a[i].y); sort(a+1,a+n+1); Solve(1,n); cout<<Ans<<endl; return 0; }