zoukankan      html  css  js  c++  java
  • bzoj3343 教主的魔法【分块入门】By cellur925

    题意:维护一个数列,给出维护区间加法,询问区间内大于等于某个值的元素个数。

    算法:分块。因为本题第二问显然可以用二分的思想,但是这貌似并不符合区间可加性,线段树好像就不好用了呢。所以本蒟蒻学习了分块。

    这大概是本蒟蒻的第一题正式分块,思想是在hzwer学长的分块入门学的==。

    什么是分块?我们维护数列(貌似树上也可以)信息时可以先采用分治的思想,把数列分成若干连续的块,维护信息可以统一在块上进行维护。假如有一个n元素的数列,根据均值不等式的数学知识(并不会证明),我们把每个块的大小设为根号n可以达到最右效果。但是显然有时根号n并不是整数==,没有关系,我们的数列上大部分都在整块上,只有小部分被遗弃的元素在不完整的块中。

    我们进行区间操作时,把在整块中的元素进行整体操作,那些在不完整的块中的元素进行暴力操作

    总结来说,就是“大块维护,小段朴素”,这也是分块最基本的思想。

    分块的复杂度(根号算法)虽然较其他数据结构可能略高了一些,却是打暴力的好方法,思维较简单,处理问题的范围更广,码量...可能略大(?)

    回到本题的两个操作:

    区间加法:对于一段区间,如果存在被整块覆盖的情况,直接在整块的加法标记上记录,不需在原序列上改动,查询时在加上加法标记;而在不完整的块中的元素,直接暴力修改。

    查询大于等于X的元素个数:想一想如果数列是无序的,我们分的块就完全没用了。但是如果我们在预处理时以及修改时对每个整块进行排序,查询时调用lower_bound/手写二分查找,问题就能轻松得到解决。那些在不完整块中的元素,同理,暴力枚举统计。

    综上,我们不难发现,使用分块算法需要或者只需要想的两个关键:

      怎么维护整块?怎么维护不完整的块?

    分块的更多代码实现细节:

      (几乎每到分块题都要的)bl[i]记录i在第几个块

      bl[i]*blo(blo=sqrt(n))可以得出当前位置(块的右边界),在此基础上变形

      右边界:取min,bl[l]*blo,n(防止越界)

    Code

     1 // luogu-judger-enable-o2
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<vector>
     6 
     7 using namespace std;
     8 
     9 int n,Q,blo;
    10 int val[1000090],bl[1000090],addtag[1000090];
    11 char opt[5];
    12 vector<int>cnt[5000];
    13 
    14 void update(int x)
    15 {
    16     cnt[x].clear();
    17     for(int i=(x-1)*blo+1;i<=min(x*blo,n);i++)
    18         cnt[x].push_back(val[i]);
    19     sort(cnt[x].begin(),cnt[x].end());
    20 }
    21 
    22 void add(int l,int r,int p)
    23 {
    24     if(l==r) val[l]+=p;
    25     else
    26     {
    27         for(int i=l;i<=min(bl[l]*blo,r);i++)
    28         val[i]+=p;
    29         update(bl[l]);    
    30         if(bl[l]!=bl[r])
    31         {
    32             for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    33                 val[i]+=p;
    34             update(bl[r]);
    35         }
    36         for(int i=bl[l]+1;i<=bl[r]-1;i++)
    37             addtag[i]+=p;
    38     }
    39 }
    40 
    41 int query(int l,int r,int p)
    42 {
    43     int ans=0;
    44     for(int i=l;i<=min(bl[l]*blo,r);i++)
    45         if(val[i]+addtag[bl[l]]>=p) ans++;
    46     if(bl[l]!=bl[r])
    47     {
    48         for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    49             if(val[i]+addtag[bl[r]]>=p) ans++;
    50     }
    51     for(int i=bl[l]+1;i<=bl[r]-1;i++)
    52     {
    53         int tmp=p-addtag[i];
    54         int pos=lower_bound(cnt[i].begin(),cnt[i].end(),tmp)-cnt[i].begin();
    55            ans+=blo-pos;
    56 //          ans+=find(i,tmp);
    57     }
    58     return ans;
    59 }
    60 
    61 int main()
    62 {
    63     scanf("%d%d",&n,&Q);
    64     for(int i=1;i<=n;i++) scanf("%d",&val[i]);
    65     blo=sqrt(n);
    66     for(int i=1;i<=n;i++)
    67     {
    68         bl[i]=(i-1)/blo+1;
    69         cnt[bl[i]].push_back(val[i]);
    70     } 
    71     for(int i=1;i<=bl[n];i++)
    72         sort(cnt[i].begin(),cnt[i].end());
    73     while(Q--)
    74     {
    75         scanf("%s",opt+1);
    76         int x=0,y=0,z=0;
    77         scanf("%d%d%d",&x,&y,&z);
    78         if(opt[1]=='M')
    79             add(x,y,z);
    80         else if(opt[1]=='A')
    81             printf("%d
    ",query(x,y,z));
    82     }
    83     return 0;
    84 }
    代码纯享版
     1 // luogu-judger-enable-o2
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<vector>
     6 
     7 using namespace std;
     8 
     9 int n,Q,blo;
    10 int val[1000090],bl[1000090],addtag[1000090];
    11 char opt[5];
    12 vector<int>cnt[5000];
    13 
    14 void update(int x)//针对散块 
    15 {//清空,重新读入 
    16     cnt[x].clear();
    17     for(int i=(x-1)*blo+1;i<=min(x*blo,n);i++)
    18         cnt[x].push_back(val[i]);
    19     sort(cnt[x].begin(),cnt[x].end());
    20 }
    21 
    22 void add(int l,int r,int p)
    23 {
    24     if(l==r) val[l]+=p;
    25     else
    26     {
    27         for(int i=l;i<=min(bl[l]*blo,r);i++)
    28             val[i]+=p;//左散块 
    29         update(bl[l]);//重新排序    
    30         if(bl[l]!=bl[r])
    31         {//右散块 
    32             for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    33                 val[i]+=p;
    34             update(bl[r]);
    35         }
    36         //整块 
    37         for(int i=bl[l]+1;i<=bl[r]-1;i++)
    38             addtag[i]+=p;
    39     }
    40 }
    41 
    42 int query(int l,int r,int p)
    43 {
    44     int ans=0;
    45     //左半散块 
    46     for(int i=l;i<=min(bl[l]*blo,r);i++)
    47         if(val[i]+addtag[bl[l]]>=p) ans++;
    48     if(bl[l]!=bl[r])// !
    49     {//右半散块 
    50         for(int i=(bl[r]-1)*blo+1;i<=r;i++)
    51             if(val[i]+addtag[bl[r]]>=p) ans++;
    52     }
    53     for(int i=bl[l]+1;i<=bl[r]-1;i++)//整块 
    54     {
    55         int tmp=p-addtag[i];//求>=p,则先把加法标记减去 
    56         int pos=lower_bound(cnt[i].begin(),cnt[i].end(),tmp)-cnt[i].begin();
    57            ans+=blo-pos;//注意这句 精髓 
    58 //          ans+=find(i,tmp);
    59     }
    60     return ans;
    61 }
    62 
    63 int main()
    64 {
    65     scanf("%d%d",&n,&Q);
    66     for(int i=1;i<=n;i++) scanf("%d",&val[i]);
    67     blo=sqrt(n);
    68     for(int i=1;i<=n;i++)
    69     {
    70         bl[i]=(i-1)/blo+1;
    71         cnt[bl[i]].push_back(val[i]);//记录每个块中元素 
    72     } 
    73     //对每个块中元素进行排序 
    74     for(int i=1;i<=bl[n];i++)
    75         sort(cnt[i].begin(),cnt[i].end());
    76     while(Q--)
    77     {
    78         scanf("%s",opt+1);
    79         int x=0,y=0,z=0;
    80         scanf("%d%d%d",&x,&y,&z);
    81         if(opt[1]=='M')
    82             add(x,y,z);
    83         else if(opt[1]=='A')
    84             printf("%d
    ",query(x,y,z));
    85     }
    86     return 0;
    87 }
    贴心注释版

    然鹅不吸氧会T掉一个点,可能是我lowerbound常数的锅锅?讨论里有人说要用longlong结果我用了longlong只会带来无端的CE/WA/RE。

    Just be cautious.

  • 相关阅读:
    codevs 1115 开心的金明
    POJ 1125 Stockbroker Grapevine
    POJ 2421 constructing roads
    codevs 1390 回文平方数 USACO
    codevs 1131 统计单词数 2011年NOIP全国联赛普及组
    codevs 1313 质因数分解
    洛谷 绕钉子的长绳子
    洛谷 P1276 校门外的树(增强版)
    codevs 2627 村村通
    codevs 1191 数轴染色
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9642951.html
Copyright © 2011-2022 走看看