bzoj 4237: 稻草人

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\n",ans);
}

int main()
{
	work();
	return 0;
}

posted @ 2017-10-20 22:55  Hxymmm  阅读(248)  评论(0编辑  收藏  举报