分块

分块

简单理解一下,分块就是优雅的暴力。

分块的分类

  1. 静态分块(只做查询,预处理):

静态分块指的是放一些关键点,预处理关键点到关键点的信息,来加速查询的,不能支持修改。

目前认为,如果可以离线,静态分块是莫队算法的子集。

  1. 动态分块(支持修改和查询):

动态分块指的是把序列分为一些块,每块维护一些信息,可以支持修改。

分块基础

对于一个长度为 \(n\) 的序列,我们可以分为一些块,每块维护一些信息。

我们把每次操作完整覆盖的块定义为“完整的块”。

把每次操作不完整覆盖的块定义为“不完整的块”。

每次操作最多经过 \(O(\sqrt{n})\) 个块,以及 \(O(1)\) 个零散块。

所以我们可以 \(O(1)\) 维护整块信息,\(O(\sqrt{n})\) 查询零散块信息。

这样就达到了 \(O(m\sqrt{n})\) 的复杂度。

分块的本质

一个度为 \(\sqrt{n}\),只有 \(3\) 层的树。

每次修改只需要更新 \(\sqrt{n}\)size\(1\) 的节点,以及 \(2\)size\(\sqrt{n}\) 的节点。

注意到我们不用维护那个 size\(n\) 的根节点的信息。

建立分块序列

\(B\) 表示每一块有多大,\(num\) 表示一共有多少块,\(belong_i\) 表示原序列中的第 \(i\) 个元素属于哪一块,\(l_i\)\(r_i\) 表示第 \(i\) 块的左右边界。

B = sqrt(n);
num = n / B + (n % B != 0);
for (int i = 1; i <= n; i ++ ) {
    belong[i] = (i - 1) / B + 1;
    l[i] = (i - 1) * B + 1;
    r[i] = i * B;
}
r[num] = n;

区间修改

for (int i = l; i <= min(r, belong[l] * B); i ++ ) a[i] += c;
if (belong[l] != belong[r]) for (int i = (belong[r] - 1) * B + 1; i <= r; i ++ ) a[i] += c;
for (int i = belong[l] + 1; i < belong[r]; i ++ ) add[i] += c;

单点查询

a[i] + add[belong[i]];

区间查询

for (int i = l; i <= min(r, belong[l] * B); i ++ ) res += a[i] + add[belong[l]];
if (belong[l] != belong[r]) for (int i = (belong[r] - 1) * B + 1; i <= r; i ++ ) res += a[i] + add[belong[r]];
for (int i = belong[l] + 1; i < belong[r]; i ++ ) res += sum[i] + add[i] * B;
posted @ 2024-11-23 11:16  zla_2012  阅读(4)  评论(0编辑  收藏  举报