• BZOJ4237 稻草人


    4237: 稻草人

    Time Limit: 40 Sec  Memory Limit: 256 MB

    Description

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

    Input

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

    Output

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

    Sample Input

    4
    0 0
    2 2
    3 4
    4 3

    Sample Output

    3

    HINT

    所有满足要求的田地由下图所示:
     
    1<=N<=2*10^5
    0<=Xi<=10^9(1<=i<=N)
    0<=Yi<=10^9(1<=i<=N)
    Xi(1<=i<=N)互不相同。
    Yi(1<=i<=N)互不相同。
     
      试一试粉字会不会太奇怪
      正解竟然是CDQ?我又没看出来……真TM菜。
      似乎这种平面上点关系计数问题 都是分治来做?很有道理啊。
      想到了分治之后还是蛮好搞清楚的。
      先随便把某一维分治,我这里拿y举例。
      按y分治后,答案就分两种情况:
        1.在同一边的,显然的递归处理。
        2.不在同一边的。那么必然是左下角在下半边,右上角在上半边。
      我们只需要考虑情况2的答案计数。
      肯定是要枚举一个角。为了方便枚举右上角。
      答案受到上、下部分的限制。
      1.上半部分:
        这个点受同在上半部分的、x,y坐标都小于它的点约束。
        如果你可以保证x有序递增,那么就只有y坐标的约束了。
        想一下就很清楚,它只受它左边第一个这样的点(x',y')的限制。
        因为统计答案要过中线,你下半部分的点的y肯定小于y'的。
        所以下半部分的点x过了x'就是不合法了。
        那么,找到往左边第一个y‘小于y的点 -> 单调栈。
        只需要在上半部分维护一个y递增的单调栈就可以解决这个约束。
      2.下半部分:
        下半部分的点合法,不考虑上半部分的点的限制的话,要满足如下条件:
        在它右上角没有点(横坐标在x之前)。
        既然如此,对于两个贡献点(x1,y1)和(x2,y2),若(x1<y1),则一定有(y1>y2)。
        维护一个y单调递减的东西即可。
        因为它的操作只有加点和删栈顶,所以也叫单调栈。
      最后统计答案的时候,在下面的单调栈内二分找到大于x'的点有多少个就可以了。
      CDQ传下去的时候y有序,传上来的时候x有序。
      窝的CDQ又进第一版辣辣辣辣!
      看来手写二分比lower_bound快啊。
    #include    <iostream>
    #include    <cstdio>
    #include    <cstdlib>
    #include    <algorithm>
    #include    <cstring>
    #define LL long long int
    #define dob double
    using namespace std;
     
    const int N = 200010;
    int n,m,tot1,tot2;LL Ans;
    struct Data{
      int x,y;
      bool operator <(const Data &i)const{
        return y<i.y;
      }
    }scr[N],st1[N],st2[N],t[N];
     
    inline int gi(){
      int x=0,res=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
      while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
      return x*res;
    }
     
    /*
    CDQ按上下(y)分治。传下去时y有序,传上去时x有序。
    枚举上半部分的点,求它的贡献?
    1.考虑上半部分的限制
    这个点受左边y坐标小于它的点约束,且只考虑往左边第一个点x'就可以了。
    因为你要越中线。过了x'就是不合法。
    找到左边第一个y小于它的点 -> 单调栈。
    在上半部分维护一个y递增的单调栈即可。
    2.考虑下半部分的限制
    下半部分的点合法,它在y以内的右上角一定没有点。
    画出合法曲线,发现它的y是单调递减的。
    随便维护一个单调递减,找的时候二分/lower_bound一下就好。
    上下要一起枚举一起加点,因为"在y以内的右上角"。
    */
     
    inline void insert1(Data Is){//下半部分,单调递减栈
      while(tot1 && st1[tot1]<Is)tot1--;
      st1[++tot1]=Is;
    }
     
    inline void insert2(Data Is){
      while(tot2 && Is<st2[tot2])tot2--;
      st2[++tot2]=Is;
    }
     
    //二分找下半部分x比nx大的点数
    inline int getl(int nx){
      int l=1,r=tot1,ans=r+1;
      while(l<=r){
        int mid=(l+r)>>1;
        if(st1[mid].x>nx)ans=mid,r=mid-1;
        else l=mid+1;
      }
      return ans;
    }
     
    inline int calc(Data Is){
      insert2(Is);
      if(tot2==1)return tot1;
      if(!tot1)return 0;
      Data Ls=st2[tot2-1];
      int l=getl(Ls.x);
      return tot1-l+1;
    }
     
    inline void CDQ(int l,int r){
      if(l==r)return;
      int mid=(l+r)>>1;
      CDQ(l,mid);CDQ(mid+1,r);
      int i=l,j=mid+1,k=l;
      tot1=0;tot2=0;
      while(i<=mid && j<=r){
        if(scr[i].x<scr[j].x){
          insert1(scr[i]);
          t[k++]=scr[i++];
        }
        else{
          Ans+=calc(scr[j]);
          t[k++]=scr[j++];
        }
      }
      while(i<=mid)t[k++]=scr[i++];
      while(j<=r){
        Ans+=calc(scr[j]);
        t[k++]=scr[j++];
      }
      for(k=l;k<=r;++k)scr[k]=t[k];
    }
     
    int main()
    {
      n=gi();
      for(int i=1;i<=n;++i){
        scr[i].x=gi();scr[i].y=gi();
      }
      sort(scr+1,scr+n+1);
      CDQ(1,n);
      printf("%lld
    ",Ans);
      return 0;
    }
    

      

  • 相关阅读:
    分享memcache和memcached安装过程(转)
    ios画图总结
    mac os下通过命令行的方式编译c++代码并在xcode里引用
    ubuntu访问windows共享文件夹
    为iphone 及iphone simulator编译boost库
    ubuntu 11.10 x64 安装oracle 11gR2时碰到的问题及解决方法
    模拟器与真机下ffmpeg的编译方法(总结版)
    ios 在View里绘图
    memcached1.4.4在ubuntu下编译的注意事项
    Google Code 服务试用
  • 原文地址:https://www.cnblogs.com/fenghaoran/p/7420620.html
Copyright © 2020-2023  润新知