如果要询问的某个纵坐标为inf的点左边是否有点能与其构成所要求的矩形,只要用个单调栈就可以了。可以想到用分治来制造单调性。
按横坐标排序,每次考虑跨过分治中心的矩形。考虑右边的每个点能与左边的哪些点构成矩形。首先这受到右边点的限制,对于每个点用set求出这个范围。然后对所有点按纵坐标从大到小排序,维护一个树状数组,如果是右边的点直接在树状数组上的该范围查询,左边的点则将其加入单调栈并在树状数组上修改。
常数过大,在darkbzoj上跑了30s,bzoj上T掉了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<set> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 200010 #define inf 1000000000 int n,stk[N],b[N],tree[N]; long long ans=0; struct data{int x,y,d; }a[N]; set<int> f; bool cmp(const data&a,const data&b) { return a.x<b.x; } bool cmp1(const data&a,const data&b) { return a.y>b.y; } void add(int k,int x){while (k<=n+1) tree[k]+=x,k+=k&-k;} int query(int k){int s=0;while (k) s+=tree[k],k-=k&-k;return s;} void solve(int l,int r) { if (l==r) return; int mid=l+r>>1; solve(l,mid); solve(mid+1,r); for (int i=mid+1;i<=r;i++) a[i].d=*f.lower_bound(a[i].y),f.insert(a[i].y); for (int i=mid+1;i<=r;i++) f.erase(a[i].y); sort(a+l,a+mid+1,cmp1); sort(a+mid+1,a+r+1,cmp1); int x=l-1,top=0; for (int i=mid+1;i<=r;i++) { while (x<mid&&a[x+1].y>a[i].y) { x++; while (top&&a[x].x>a[stk[top]].x) add(a[stk[top]].y,-1),top--; stk[++top]=x;add(a[x].y,1); } ans+=query(a[i].d)-query(a[i].y); } x=l-1,top=0; for (int i=mid+1;i<=r;i++) { while (x<mid&&a[x+1].y>a[i].y) { x++; while (top&&a[x].x>a[stk[top]].x) add(a[stk[top]].y,1),top--; stk[++top]=x;add(a[x].y,-1); } } sort(a+l,a+mid+1,cmp); sort(a+mid+1,a+r+1,cmp); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4237.in","r",stdin); freopen("bzoj4237.out","w",stdout); const char LL[]="%I64d "; #else const char LL[]="%lld "; #endif n=read(); for (int i=1;i<=n;i++) a[i].x=read(),b[i]=a[i].y=inf-read(); sort(b+1,b+n+1); for (int i=1;i<=n;i++) a[i].y=lower_bound(b+1,b+n+1,a[i].y)-b; sort(a+1,a+n+1,cmp);f.insert(n+1); solve(1,n); cout<<ans; return 0; }