分块算法简单介绍(一)

分块算法,一开始看到时总感觉它很高级,学过之后才知道它比线段树还好理解.

分块算法其实就是一个稍微优美一点的暴力算法.

分块算法的思想就是把一个数列分成很多块来处理,每当区间操作完整包括整块时直接在块的标记上处理,否则暴力处理.

就像一个数列[1..1000000],分块算法的思想就是把这个数列拆成[1..1000],[1001..2000],[2001..3000],...,[1000*n+1..1000*(n+1)],...,[999001..1000000]这些块,然后维护每一个块的标记.

一般来说假设一个数列有n个元素的话,就拆成sqrt(n)块来处理,这样份快递效率最坏情况下大概是O(3*sqrt(n)),省略常数后就是O(sqrt(n)),最稳定.

至于怎么维护标记,就如线段树.

其实我们可以把线段树看成把一个序列分成两块,然后再把他的子块分别分成两块,然后再分...

我们就来说说怎么用分块来维护区间加,单点查询.

首先分块,每块的长度我用了200.

每块维护一个标记bl[i],表示第i个块每个数都加有的数是多少.

然后我们考虑如何区间加.

区间[L..R]有两种情况:1.包含完整的块;2.不包含完整的块.

怎么判断呢?直接把指针L往R移,不停的L++,直到L是一个块的左边界或触碰到了R.

前者的条件是L mod bls=1(一下用bls代表每个块的长度),后者的条件是L>R.

之后用R往L移,直到R是一个块的右边界或触碰到了L.

前者的条件是R mod bls=0,后者的条件是L<R.

若这是L>R,就可以return了.

不然你就修改块了.

代码如下:

inline void add(int L,int R,int num){
  for (;L%200!=1&&L<=R;L++)
    p[L]+=num;
  for (;R%200!=0&&R>=L;R--)
    p[R]+=num;
  if (R<L) return;
  for (int i=L/200+1;i<=R/200;i++)
    bl[i]+=num;
}

不用L执行完判断一次,R执行完判断一次,因为假如L执行完R<L了,那么R就不会执行了.

然后就是查询了.

查询的话直接把它的值与他所处块的标记相加就行了.

代码如下:

inline int query(int X){
  return p[X]+bl[(X-1)/200+1];
}

这就是分块的简单讨论了.

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/79821436
今日推荐