Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • 线上赛板子(实时更新)
  • 数据结构

    • 堆
    • 并查集
    • 树状数组
    • 分块
      • 算法思想
      • 实现
    • 树相关
    • 线段树
    • 平衡树
    • 树链剖分
    • LCT
    • 最近公共祖先
    • 虚树
    • 树分治
    • K-D Tree
    • 笛卡尔树
    • 珂朵莉树
  • 数学

  • 计算几何

  • 动态规划

  • 图论

  • 字符串

  • 杂项

  • 算法笔记
  • 数据结构
martian148
2024-09-03
目录

分块

# 分块

# 算法思想

分块不是一种数据结构,而是一种思想

通过对原数据的适当划分,并在划分后的每一个块上预处理部分信息,从而较一般的暴力算法取得更优的时间复杂度。

image.png

# 实现

具体来说分块的基本要素如下:

  1. 块的大小:blockblockblock,一般情况下都定义 block=nblock=\sqrt{n}block=n​
  2. 块的数量:numnumnum
  3. 块的左右边界,定义数组 st,edst,edst,ed,分别表示第 iii 个块的第一个和最后一个元素的位置,显然有 st[1]=1,ed[1]=block,st[2]=block+1,ed[2]=2×block,st[i]=(i−1)×block+1,ed[i]=i×blockst[1]=1,ed[1]=block,st[2]=block+1,ed[2]=2\times block,st[i]=(i-1)\times block+1,ed[i]=i\times blockst[1]=1,ed[1]=block,st[2]=block+1,ed[2]=2×block,st[i]=(i−1)×block+1,ed[i]=i×block
  4. 每个元素属于的块 belong[i]belong[i]belong[i] 表示第 iii 个元素所在的块,有 belong[i]=(i−1)/block+1belong[i]=(i-1)/block+1belong[i]=(i−1)/block+1

下面是常见分块代码实现

    int block = sqrt(n), t = n / block + (n % block != 0); // t 表示块数
    for (int i = 1; i <= t; i++)
        st[i] = (i - 1) * block + 1, ed[i] = i * block;
    ed[t] = n;
    for (int i = 1; i <= n; i++)
        belong[i] = (i - 1) / block + 1;
1
2
3
4
5
6

用分块解决区间问题很方便,定义区间有关的数组,

例如我们需要求区间和,就定义 sum[i]sum[i]sum[i] 表示第 iii 个块的 aaa 的和,并预处理出初始值

for (int i = 1; i <= num; i++)
        for (int j = st[i]; j <= ed[i]; j++)
            sum[j] += a[j];
1
2
3

然后 add[i]add[i]add[i] 表示第 iii 个块的增量标记,初始为 000

考虑区间修改:将 [L,R][L,R][L,R] 内每个数加上 ddd

  • 如果区间 [L,R][L,R][L,R] 在某个块内,直接暴力修改,然后更新 sum[i]=sum[i]+d(R−L+1)sum[i]=sum[i]+d(R-L+1)sum[i]=sum[i]+d(R−L+1),计算次数为 n/tn/tn/t
  • 如果跨越了多个块,假设被 [L,R][L,R][L,R] 包含了 kkk 个块,更新 add[i]+=dadd[i]+=dadd[i]+=d 对于两头没有完全包含的就暴力处理,计算次数为 n/t+kn/t+kn/t+k

考虑区间查询:输出 [L,R][L,R][L,R] 内每个数的和

  • 区间 [L,R][L,R][L,R] 在一个块内,暴力累计,最后加上 add[i]add[i]add[i] ,答案就是 ∑i=LRai+add[i]×(R−L+1)\sum\limits_{i=L}^R a_i+add[i]\times (R-L+1)i=L∑R​ai​+add[i]×(R−L+1),计算次数为 n/tn/tn/t
  • 区间 [L,R][L,R][L,R] 跨越了多个块,在被 [L,R][L,R][L,R] 那些完全包含的块内,and+=sum[i]+add[i]×len[i]and+=sum[i]+add[i]\times len[i]and+=sum[i]+add[i]×len[i],两头的情况暴力处理 ,计算次数为 n/t+kn/t+kn/t+k

所以块的大小 blockblockblock 为多少时最优,每次操作的次数为 n/t+kn/t+kn/t+k 或 n/tn/tn/t,kkk 最大 =t=t=t,最差时间复杂度为 O(n/t+t)O(n/t+t)O(n/t+t) 按照基本不等式 t=nt=\sqrt{n}t=n​ 时最优,此时 block=n/t=nblock=n/t=\sqrt{n}block=n/t=n​

上次更新: 2025/04/08, 18:03:31
树状数组
树相关

← 树状数组 树相关→

最近更新
01
Java基础语法
05-26
02
开发环境配置
05-26
03
pink 老师 JavaScript 学习笔记
05-26
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式