Description

JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典。
有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地。和启示中的一样,田地需要满足以下条件:
田地的形状是边平行于坐标轴的长方形;
左下角和右上角各有一个稻草人;
田地的内部(不包括边界)没有稻草人。
给出每个稻草人的坐标,请你求出有多少遵从启示的田地的个数

Input

第一行一个正整数N,代表稻草人的个数
接下来N行,第i(1<=i<=N)包含2个由空格分隔的整数XiYi,表示第i个稻草人的坐标

Output

输出一行一个正整数,代表遵从启示的田地的个数

Sample Input

4
0 0
2 2
3 4
4 3

Sample Output

3

HINT

所有满足要求的田地由下图所示:

1N2×105
0Xi109(1iN)
0Yi109(1iN)
Xi(1iN)互不相同。
Yi(1iN)互不相同。

Source

JOI 2013~2014 春季training合宿 竞技3 By PoPoQQQ

思路

cdq分治。
首先按x排序;
对于两个都在中心一侧的点,递归处理;
然后就只要讨论分居中心左右的点了。
对左右按y排序;
左边维护一个单调下降的栈,右边维护一个单调上升的栈,以右边为“自变量”,每次右边加一,往左边一直加,直到左边的下一个的y坐标大于右边位置;
现在右边单调上升的栈的栈顶的元素所能构成的矩形个数,就是左边的栈中y坐标右侧栈顶与栈的自顶向下第二个元素之间的点之间的元素个数。

代码

#include <cstdio>
#include <algorithm>
#include <iostream>

const int maxn=200000;

template<typename t>
struct stack
{
  t c[maxn+10];
  int head;

  inline int push(t x)
  {
    ++head;
    c[head]=x;
    return 0;
  }

  inline int pop()
  {
    --head;
    return 0;
  }

  inline t top()
  {
    return c[head];
  }

  inline int size()
  {
    return head;
  }

  inline int clear()
  {
    head=0;
    return 0;
  }
};

struct point
{
  int x,y;

  inline int make_point(int x_,int y_)
  {
    x=x_;
    y=y_;
    return 0;
  }
};

point d[maxn+10];
stack<point> smaller,bigger;
long long ans;
int n;

bool cmpx(const point &a,const point &b)
{
  return a.x<b.x;
}

bool cmpy(const point &a,const point &b)
{
  return a.y<b.y;
}

int solve(int l,int r,int pl,int pr)
{
  if((l==r)||(pl>pr))
    {
      return 0;
    }
  std::sort(d+pl,d+pr+1,cmpx);
  int mid=(l+r)>>1,pmid=0;
  if(d[pl].x>mid)
    {
      solve(mid+1,r,pl,pr);
      return 0;
    }
  if(d[pr].x<=mid)
    {
      solve(l,mid,pl,pr);
      return 0;
    }
  for(register int i=pl+1; i<=pr; ++i)
    {
      if(d[i].x>mid)
        {
          solve(l,mid,pl,i-1);
          solve(mid+1,r,i,pr);
          pmid=i-1;
          break;
        }
    }
  std::sort(d+pl,d+pmid+1,cmpy);
  std::sort(d+pmid+1,d+pr+1,cmpy);
  int i=pmid+1,j=pl;
  smaller.clear();
  bigger.clear();
  while((i<=pr)||(!bigger.size()))
    {
      while((bigger.size())&&(bigger.top().x>d[i].x))
        {
          bigger.pop();
        }
      point prep=bigger.top();
      bigger.push(d[i]);
      while((j<=pmid)&&(d[j].y<d[i].y))
        {
          while((smaller.size())&&(smaller.top().x<d[j].x))
            {
              smaller.pop();
            }
          smaller.push(d[j]);
          ++j;
        }
      int left=1,right=smaller.size(),res=0;
      while(left<=right)
        {
          int m=(left+right)>>1;
          if(smaller.c[m].y>=prep.y)
            {
              right=m-1;
            }
          else
            {
              left=m+1;
              res=m;
            }
        }
      ans+=(bigger.size()==1)?smaller.size():(smaller.size()-res);
      ++i;
    }
  return 0;
}

int main()
{
  scanf("%d",&n);
  for(register int i=1; i<=n; ++i)
    {
      scanf("%d%d",&d[i].x,&d[i].y);
    }
  solve(0,1000000000,1,n);
  printf("%I64d\n",ans);
  return 0;
}