Description
JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。
有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:
田地的形状是边平行于坐标轴的长方形;
左下角和右上角各有一个稻草人;
田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数
solution
正解:CDQ分治.
我们分析满足 (x[i]<x[j],y[i]<y[j]),并且不存在 (k),(x[i]<x[k]<x[j],y[i]<y[k]<y[k])的算作一个满足条件的点对.
以上二维偏序可以用分治求解,我们可以分治 (x).
分治中按 (y) 排序,我们考虑 (mid) 左边对 (mid) 右边的贡献:
左边是作为矩形的左下角,右边作为右上角.
我们要意识到,在 (mid) 右边 对当前点 (i) 影响最大的一定是 (y) 比 (i) 小,(x) 比 (i) 也小的点,因为 (i) 是作为矩形的右上角,所以这个的点会成为矩形中的点,不会形成矩形,所以我们维护一个 (x) 递增的单调栈,左边维护一个 (x) 递减的单调栈,当前右边的点 (i) ,能够和左边满足(y<=y[i],y>=y[st[top]])的点匹配,然后每一次在左边栈中二分即可,画个图很好理解
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=200005;
int n,st[N],top=0,xy[N],pi=0;long long ans=0;
struct node{
int x,y;
}a[N];
bool comp(const node i,const node j){return i.x<j.x;}
bool camp(const node i,const node j){return i.y<j.y;}
void solve(int l,int r){
if(l==r)return ;
int mid=(l+r)>>1;
solve(l,mid);solve(mid+1,r);
top=0;pi=0;int j=l,li;
sort(a+l,a+mid+1,camp);sort(a+mid+1,a+r+1,camp);
for(int i=mid+1;i<=r;i++){
while(a[j].y<=a[i].y && j<=mid){
while(top && a[j].x>=a[st[top]].x)top--;
st[++top]=j;j++;
}
while(pi && a[i].x<a[xy[pi]].x)pi--;
xy[++pi]=i;li=a[xy[pi-1]].y;
int L=1,R=top,mi,ret=-1;
while(L<=R){
mi=(L+R)>>1;
if(a[st[mi]].y>=li)ret=mi,R=mi-1;
else L=mi+1;
}
if(ret!=-1)ans+=top-ret+1;
}
}
void work()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);
sort(a+1,a+n+1,comp);
solve(1,n);
printf("%lld
",ans);
}
int main()
{
work();
return 0;
}