本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!
題目鏈接:BZOJ4237
正解:$CDQ$分治
解題報告:
考慮這種兩維的題目,想辦法用$CDQ$分治降維然后變成序列問題。
對$y$坐標分治,每次分成兩半,我只考慮上面一半的點對下面的影響(也就是田地的左下角在下面一半,右上角在上面一半)。
對於上下的內部分別按$x$排序,我枚舉上面的點,考慮以它為右上角的矩形個數:顯然這個矩形要受上半部分中離它最近的$x$、$y$都比它小的點的制約,同時下半部分的點也需要滿足之間沒有別的點,縱坐標已經滿足條件了,只需要考慮橫坐標即可。
根據上面的條件,不難看出只需要對上半部分維護一個縱坐標單增(縱坐標大於它的無法約束)的單調棧,下半部分維護一個縱坐標單減的單調棧,每次枚舉上面的點的時候,把橫坐標小於它的下半部分的點全部加入下面的單調棧,在下面的單調棧二分一下就能得到答案了。
//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦關終屬楚;苦心人,天不負,卧薪嘗膽,三千越甲可吞吳。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 200011;
int n,stack[MAXN],top,stack2[MAXN],tail;
LL ans;
struct node{ int x,y; }a[MAXN];
inline bool cmpx(node q,node qq){ return q.x<qq.x; }
inline bool cmpy(node q,node qq){ return q.y<qq.y; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
inline void solve(int l,int r){
if(l==r) return ; sort(a+l,a+r+1,cmpy); int mid=(l+r)>>1;
sort(a+l,a+mid+1,cmpx);//down
sort(a+mid+1,a+r+1,cmpx);//up
top=tail=0; int now=l,L,R,pos,mm,cp;
for(int i=mid+1;i<=r;i++) {
while(top>0 && a[stack[top]].y>=a[i].y) top--;
stack[++top]=i;
while(now<=mid && a[now].x<a[i].x) {
while(tail>0 && a[stack2[tail]].y<=a[now].y) tail--;
stack2[++tail]=now;
now++;
}
L=1; R=tail; pos=-1; cp=a[stack[top-1]].x;
while(L<=R) {
mm=(L+R)>>1;
if(a[stack2[mm]].x>cp) pos=mm,R=mm-1;
else L=mm+1;
}
if(pos!=-1) ans+=tail-pos+1;
}
solve(l,mid); solve(mid+1,r);
}
inline void work(){
n=getint(); for(int i=1;i<=n;i++) a[i].x=getint(),a[i].y=getint();
a[0].x=a[0].y=-1;
solve(1,n);
printf("%lld",ans);
}
int main()
{
work();
return 0;
}
//有志者,事竟成,破釜沉舟,百二秦關終屬楚;苦心人,天不負,卧薪嘗膽,三千越甲可吞吳。
