zoukankan      html  css  js  c++  java
  • HDU 4391 Paint The Wall [分块哈希]

      给出一个区间以及两种区间操作,[1 l,r,c]把l~r的点染成c色,[2,l,r,c]查询l~r内颜色为c的点的数目。

      因为c的数目太大,用线段树是存不下的,所以只能另辟蹊径了。虽然网上有线段树的方法,但那个Max-Min剪枝完全是没有作用的,随便出一组数据,把所有点间隔染成3,5,然后查4,这样用线段树不知道要跑多久。。。

      题解说这题是裸的分块HASH。。还没写过,学习了。其实就是将原区间划分乘sqrt(n)个区间,每次暴力查询和跟新两边的区间,中间的区间直接用hash存每种颜色的节点的数量。这里用到了类似线段树的lazy思想,区间成段修改直接打个标记,等到要划分这个区间的时候先把标记传下去,然后跟新。

      复杂度可以保证在每次操作sqrt(n)左右,这里为了方便我就直接写map了。。

      

     1 #include <stdio.h>
     2 #include <math.h>
     3 #include <string.h>
     4 #include <map>
     5 #define MAXN 100005
     6 int n,m,bsize,bnum,x[MAXN];
     7 struct hash_block{
     8      int cls,size;
     9      std::map<int,int> mp;
    10 }b[350];
    11 //下传标记,当这个块化整为零的时候需要下传标记并跟新所有元素
    12 void pushdown(int id){
    13     hash_block &hb=b[id];
    14     if(hb.cls!=-1){
    15         for(int i=id*bsize;i<id*bsize+hb.size;i++)x[i]=hb.cls;
    16         hb.mp.clear(),hb.mp[hb.cls]=hb.size;
    17         hb.cls=-1;
    18     }
    19 }
    20 //跟新,中间的部分打标记就可以了,两边的sqrt(n)暴力跟新
    21 void update(int l,int r,int c){
    22     int lb=l/bsize,rb=r/bsize,ans=0;
    23     for(int i=lb+1;i<rb;i++)b[i].cls=c;
    24     if(lb!=rb){
    25         pushdown(lb);pushdown(rb);
    26         for(int i=l;i<lb*bsize+b[lb].size;i++)
    27             b[lb].mp[x[i]]--,b[lb].mp[c]++,x[i]=c;
    28         for(int i=rb*bsize;i<=r;i++)
    29             b[rb].mp[x[i]]--,b[rb].mp[c]++,x[i]=c;
    30     }else{
    31     pushdown(lb);
    32     for(int i=l;i<=r;i++)
    33         b[lb].mp[x[i]]--,b[lb].mp[c]++,x[i]=c;
    34     }
    35 }
    36 //中间的部分根据标记或者hash表可以直接查询,两边的sqrt(n)暴力查询
    37 int query(int l,int r,int c){
    38     int lb=l/bsize,rb=r/bsize,ans=0;
    39     for(int i=lb+1;i<rb;i++){
    40         //一直错在这个地方了,如果有标记,直接判断标记是不是需要的颜色
    41         //如果没标记,要先判map中有没有这个元素然后在操作,否则会MLE!
    42         if(b[i].cls==c)ans+=b[i].size;
    43         else if(b[i].cls==-1&&b[i].mp.find(c)!=b[i].mp.end())ans+=b[i].mp[c];
    44     }
    45     if(lb!=rb){
    46         pushdown(lb);pushdown(rb);
    47         for(int i=l;i<lb*bsize+b[lb].size;i++)ans+=(x[i]==c);
    48         for(int i=rb*bsize;i<=r;i++)ans+=(x[i]==c);
    49     }else{
    50         pushdown(lb);
    51         for(int i=l;i<=r;i++)ans+=(x[i]==c);
    52     }
    53     return ans;
    54 }
    55 void initblock(){
    56     bsize=(int)sqrt(n+1e-8);
    57     bnum=(n-1)/bsize+1;
    58     for(int i=0;i<bnum;i++){
    59         b[i].mp.clear();
    60         b[i].cls=-1;
    61         b[i].size=std::min(i*bsize+bsize,n)-i*bsize;
    62     }
    63     for(int i=0;i<n;i++){
    64         scanf("%d",&x[i]);
    65         b[i/bsize].mp[x[i]]++;
    66     }
    67 }
    68 int q,l,r,z;
    69 int main(){
    70     //freopen("test.in","r",stdin);
    71     while(scanf("%d%d",&n,&m)!=EOF){
    72         initblock();
    73         while(m--){
    74             scanf("%d%d%d%d",&q,&l,&r,&z);
    75             if(q==1)update(l,r,z);
    76             else printf("%d\n",query(l,r,z));
    77         }
    78     }
    79     return 0;
    80 }
  • 相关阅读:
    Codeforces Round #733
    [ZJOI2007] 时态同步(树形dp)
    最大子树和(树形dp)
    P2015 二叉苹果树
    没有上司的舞会(经典树形dp)
    P3884 [JLOI2009]二叉树问题(LCA)
    Bin Packing Problem(线段树 + multiset)
    P4281 [AHOI2008]紧急集合 / 聚会(最近公共祖先)
    P3128 [USACO15DEC]Max Flow P(LCA 树上差分)
    java中Set接口用法
  • 原文地址:https://www.cnblogs.com/swm8023/p/2698780.html
Copyright © 2011-2022 走看看