zoukankan      html  css  js  c++  java
  • bzoj 2727: [HNOI2012]双十字

    Description

    在C 部落,双十字是非常重要的一个部落标志。所谓双十字,如下面两个例子,由两条水平的和一条竖直的“1”线段组成,要求满足以下几个限制:

    我们可以找到 5 个满足条件的双十字,分别如下:

     

    注意最终的结果可能很大,只要求输出双十字的个数 mod 1,000,000,009 的值

    ·两条水平的线段不能在相邻的两行。
    ·竖直线段上端必须严格高于两条水平线段,下端必须严格低于两条水平线段。
     
    ·竖直线段必须将两条水平线段严格划分成相等的两半。
    ·上方的水平线段必须严格短于下方的水平线段。
     
    所以上面右边的例子是满足要求的最小的双十字。
    现在给定一个   R?C的01 矩阵,要求计算出这个 01 矩阵中有多少个双十字。
    例如下面这个例子,R=6,C=8,01 矩阵如下:

    Input

    第一行为用空格隔开的两个正整数 R和C,分别表示
    01矩阵的行数和列数。输入文件第二行是一个非负整数N,表示01矩阵中“0”的个数。接下来的N行,每行为用空格隔开的两个正整数x和y(1≤x≤R,1≤y≤C),表示(x,y)是一个“0”。数据保证N个“0”的坐标两两不同。
    数据保证R,C,N≤10,000,R*C≤1,000,000.(事实上R*C可能稍大于原设定)

    Output

    D mod 1,000,000,009 的结果,其中D 为要求的 01
    矩阵中双十字的个数。

    Sample Input



    6 8
    12
    1 2
    1 3
    1 4
    1 6
    2 2
    3 2
    3 3
    3 4
    3 7
    6 4
    6 6
    4 8

    Sample Output

    5

    HINT

    Source

    day1

    跟这个题断断续续鏖战了几天了,首先在考场上想了一个n^3的暴力,然后智障的枚举的是每一列。。。

    然后变成了三维偏序(j>top)并且那个东西(top不一样)还不好用树状数组维护,然后傻逼压常,结果vector葬送前程。。。

    其实枚举一个个连续的纵轴会好做很多,枚举每一个纵轴的话,在一个纵轴上top都是一样的,然后就是二维偏序,树状数组维护即可。。。

    具体做法:

    对于每个点求出heng[i],down[i],up[i],即横着扩展的长度,往下扩展的长度,往上扩展的长度。。。(我是个sb第一个数组是用二分+前缀和找的)

    然后考虑暴力的枚举两个十字的中心,然后分情况讨论一下上下的长度能有多少个组合是合法的,然后再乘以上下多余的长度。。。

    设上面的heng为l1,下面的heng为l2,上面往上扩展的长度为s,下面往下扩展的长度为x。。。

    题解链接:http://blog.csdn.net/thy_asdf/article/details/50450561(实在是不想用公式编辑器写了)

    然后计算的话就是一堆等差数列求和,记得把每一项都展开(即把只和j有关的放在一起维护,一定要细心),然后用树状数组维护一下,贼恶心。。。

    // MADE BY QT666
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #include<map>
    #include<vector>
    #define RG register
    #define int long long
    using namespace std;
    typedef long long ll;
    const int N=3000050;
    const int rhl=1000000009;
    vector<int> Map[30000],id[30000];
    int R,C,n;
    int heng[N],down[N],up[N],tt,sum[200000];
    ll ans,inv[10],tr[50050][5],T=40000,xu=10000,q[N],res;
    inline void pre(int g){
      memset(sum,0,sizeof(sum));
      for(RG int i=1;i<=C;i++) sum[i]=sum[i-1]+Map[g][i];
    }
    inline int query_left(int i){
      RG int l=1,r=i,ret=i;
      while(l<=r){
        int mid=(l+r)>>1;
        if(sum[i]-sum[mid-1]==(i-mid+1)) r=mid-1,ret=mid;
        else l=mid+1;
      }
      return i-ret;
    }
    inline int query_right(int i){
      RG int l=i,r=C,ret=i;
      while(l<=r){
        int mid=(l+r)>>1;
        if(sum[mid]-sum[i-1]==(mid-i+1)) ret=mid,l=mid+1;
        else r=mid-1;
      }
      return ret-i;
    }
    inline void work_heng(int g){
      for(int i=1;i<=C;i++){
        if(Map[g][i]){
          int l1=query_left(i),l2=query_right(i);
          heng[id[g][i]]=min(l1,l2);
        }
      }
    }
    inline void work_down(int g){
      for(RG int i=R-1;i>=1;i--){
        if(Map[i][g]){
          if(Map[i+1][g]) down[id[i][g]]=down[id[i+1][g]]+1;
        }
      }
    }
    inline void work_up(int g){
      for(RG int i=2;i<=R;i++){
        if(Map[i][g]){
          if(Map[i-1][g]) up[id[i][g]]=up[id[i-1][g]]+1;
        }
      }
    }
    inline int calc(ll sx,ll mx){return (sx+mx)*(mx-sx+1)%rhl*inv[2]%rhl;}
    inline ll qpow(ll a,ll b){ll ans=1; while (b){ if (b&1) (ans*=a)%=rhl; (a*=a)%=rhl,b>>=1; } return ans; }
    inline void update(int x,int val,int type){
      x+=xu;
      for(int i=x;i<=T;i+=i&-i) tr[i][type]+=val;
    }
    inline int query(int x,int type){
      int ret=0;x+=xu;
      for(int i=x;i;i-=i&-i) ret+=tr[i][type];
      return ret;
    }
    inline void work(int i,int g,int top,int type){
      int L=heng[id[i-1][g]];
      update(L,(i-1-top)*type,1);
      update(L,L*(i-1-top)*type,2);
      update(L,L*L*(i-1-top)*type,3);
      update(L,calc(1,L-1)*(i-1-top)*type,4);
    }
    inline void solve(int g){
      int top=1,la=1+down[id[1][g]];
      while(1){
        if(la>R) break;
        for(int i=top;i<=la;i++){
          ll l=heng[id[i][g]],x=down[id[i][g]];
          (ans+=(query(C,1)-query(l,1))%rhl*calc(1,l-1)*x%rhl)%=rhl;
          ans+=(l*query(l,2)-query(l,3)+query(l,4))*x;
          if(i>top){
        q[++res]=i;work(i,g,top,1);
          }
        }
        for(int i=1;i<=res;i++) work(q[i],g,top,-1);
        top=la+1;res=0;
        while(top<=R&&Map[top][g]==0) top++;
        la=top+down[id[top][g]];
      }
    }
    main(){
      scanf("%lld%lld%lld",&R,&C,&n);
      inv[2]=qpow(2,rhl-2);
      for(RG int i=1;i<=R;i++) Map[i].push_back(0),id[i].push_back(0);
      for(RG int i=1;i<=R;i++)
        for(RG int j=1;j<=C;j++)
          ++tt,Map[i].push_back(1),id[i].push_back(tt);
      for(int i=1;i<=C;i++) Map[R+1].push_back(0),id[R+1].push_back(0);
      for(RG int i=1;i<=n;i++){
        int x,y;scanf("%lld%lld",&x,&y);Map[x][y]=0;
      }
      for(RG int i=1;i<=R;i++) pre(i),work_heng(i);
      for(RG int i=1;i<=C;i++) work_down(i);
      for(RG int i=1;i<=C;i++) work_up(i);
      for(RG int i=1;i<=C;i++) solve(i);
      printf("%lld
    ",ans);
      return 0;
    }
    

      

  • 相关阅读:
    iOS 制作view渐变的效果CAGradientLayer
    iOS应用架构谈 view层的组织和调用方案
    xcode8 iOS10 log太多
    iOS 10 UserNotifications 框架解析
    iOS UITableViewCell 左滑删除时,修改删除按钮背景颜色,默认是红色的
    给self.navigationItem.rightBarButtonItem设置字体颜色
    iPhone屏幕尺寸、分辨率及适配
    iOS 判断输入是否全是空格
    ios 修改UItableviewcell点击时的颜色
    ios 播放语音(文字转语音) 播放音频文件 振动
  • 原文地址:https://www.cnblogs.com/qt666/p/7249609.html
Copyright © 2011-2022 走看看