序列分块入门教程

学妹让我教她分块,翻了翻以前的一篇博客,发现写的太难、太简略了,所以写了这篇博客 QAQ。

真的是分块最基础的教程,大佬可以跳过了。

如果本文有可以改进的地方,可以在评论区提出。

前言 - 序列分块是什么

顾名思义,序列分块就是把数列分成若干小块进行处理和维护,我们会把每一块当成整体,维护这些整体的有关信息。

最基础的分块当取块长为 \sqrt{n} 时的初始化复杂度是 \mathcal{O}(n),单次操作复杂度是 \mathcal{O}(\sqrt{n}),当然一些较为复杂的题需要对照题目具体分析。

例题一 - P3372 【模板】线段树 1

简要分析

区间加区间和,参考题目名称可以发现,出题人貌似想让我们写线段树,于是我们考虑如何不写线段树。我们采用分块。

变量定义

以下是代码中的一些数组或变量名称及其含义:

  • n,m:数列长度和操作次数。
  • a_i:数列第 i 项。
  • s_i:第 i 块所有数的和。
  • tag_i:第 i 块整体加法标记,详见下文。
  • pos_i:数列第 i 项所在块编号。
  • L_i:第 i 块左端点编号。
  • R_i:第 i 块右端点编号。
  • sz:块长。
  • tot:总块数。

块状数组初始化

首先取块长为 \sqrt{n},然后枚举每一块,可以知道第 i 块的左端点是第 i-1 块的右端点的下一个元素,第 i 块的右端点是 \min(i\times sz,n),我们通过这个式子对 L,R 进行初始化。

随后枚举 L_i\sim R_i 的所有元素,用 s_i 记录他们的和,同时把这些元素的 pos 标记为 i

至此,我们便完成了初始化,因为每一块的区间不重叠且没有遗漏,a 数组的每个元素下标恰好访问一遍,因此复杂度是 \mathcal{O}(n) 的。

此部分代码:

void initBlock() {
    sz = sqrt(n);
    while(++tot) {
        L[tot] = R[tot-1] + 1;
        R[tot] = min(sz*tot, n);
        rep(i, L[tot], R[tot]) pos[i] = tot, s[tot] += a[i];
        if(R[tot] == n) break;
    }
}

区间加

此部分操作要求将 a_x\sim a_y 的所有数加上 \Delta,暴力单次加是 \mathcal{O}(n) 的,显然需要优化。

对于 xy 在同一块的情况,我们暴力从 x 枚举到 y,将 a_is_{pos_{i}} 加上 \Delta。由于块长为 \sqrt{n},因此暴力枚举这一部分的复杂度为 \mathcal{O}(\sqrt{n})

对于不在同一块的情况,对两侧的零散块(不完整块,即 x,y 所在块)按照同上的方法暴力处理,对中间的每个完整块记录整体加法标记 tag,并把块中所有数和同步记录。由于块长为 \sqrt{n},不完整块最多操作次数为 2\times\sqrt{n},完整块不超过 \sqrt{n} 个,因此这一部分的复杂度为 \mathcal{O}(\sqrt{n})

综上,块状数组区间加法的复杂度为 \mathcal{O}(\sqrt{n})

此部分代码:

void modify(ll x, ll y, ll delta) {
    ll l = pos[x], r = pos[y];
    if(l == r) rep(i, x, y) a[i] += delta, s[l] += delta;
    else {
        rep(i, x, R[l]) a[i] += delta, s[l] += delta;
        rep(i, l+1, r-1) tag[i] += delta, s[i] += delta * (R[i] - L[i] + 1);
        rep(i, L[r], y) a[i] += delta, s[r] += delta;
    }
}

区间和

查询区间 a_x\sim a_y 所有数的和,同样不能暴力加,考虑按照同样的方法优化:

对于同一块的情况,暴力枚举并累加 a_i+tag_{pos_i}注意不要漏掉当前块的整体加法标记!

对于不在同一块的情况,两侧的零散块同样方法处理,中间的整块直接累加块和即可。

同样的,查询区间和的复杂度为 \mathcal{O}(\sqrt{n})

此部分代码:

ll query(ll x, ll y) {
    ll l = pos[x], r = pos[y], ans = 0;
    if(l == r) rep(i, x, y) ans += a[i] + tag[l];
    else {
        rep(i, x, R[l]) ans += a[i] + tag[l];
        rep(i, l+1, r-1) ans += s[i];
        rep(i, L[r], y) ans += a[i] + tag[r];
    }
    return ans;
}

AC 本题

将以上代码结合起来即可 AC 本题,总复杂度为 \mathcal{O}(n+m\sqrt{n})完整代码戳我

习题一 - P2357 守墓人

基本上是双倍经验,可以稍加修改通过本题。

例题二 - P2801 教主的魔法

简要分析

简要题意:区间加,查询区间内多少个数大于等于 c

我们可以采用分块,对每一块进行重构排序,然后询问时直接使用二分。

块内排序

块内排序在代码中用的比较多,因此我专门写了个函数重构块。

void reconstruct(int x) {
    rep(i, L[x], R[x]) b[i] = a[i];
    sort(b+L[x], b+1+R[x]);
}

初始化

初始化部分主要有一个更改:对于每一块,需要将该块内元素拷贝一遍并排序。

新初始化复杂度 \mathcal{O}(n\log\sqrt{n}),使用对数换底公式可以知道这个复杂度与 \mathcal{O}(n\log n) 相差为常数,可以视为 \mathcal{O}(n\log n)

区间加

区间加对零散块直接暴力加,然后进行重构;因为对整块的加法不改变块内元素顺序,打上整体加法标记即可。

复杂度 \mathcal{O}(\sqrt{n}\log\sqrt{n})=\mathcal{O}(\sqrt{n}\log n)

区间大于等于 c 数个数

零散块直接暴力查询,记得加上整体加法标记。

对于完整块,在排好序的块内二分找 c-tag_i,因为是排好序的,直接根据下标算即可。

复杂度 \mathcal{O}(\sqrt{n}\log\sqrt{n})=\mathcal{O}(\sqrt{n}\log n)

AC 本题

结合一下以上部分即可 AC 本题,总复杂度为 \mathcal{O}(n\log n+Q\sqrt{n}\log n)。看到数据范围 Q 是比较小的,可以通过。

完整代码接着戳我

习题二 - U147831 分块问题 3

区间加,求区间内 c 的前驱。

依然是块内排序、查询时二分的思想。

习题三 - U147840 分块问题 6

单点插入,单点查询。

对于每一块维护 vector,插入时遍历每一块找插入点,查询时遍历每一块用 vector 查值即可。

这一方法可以通过本题,但是因为插入的数随机生成,每一块的插入次数大致均匀。可以通过构造数据的方式卡掉这一做法。

我选择当一块的 vector 大小超过 8\times sz 的时候将所有 vector 清空并重构,因为重构的次数比较少可以通过本题。

习题四 - P4145 上帝造题的七分钟 2 / 花神游历各国

区间开平方根,区间和。

与线段树做法想法类似,维护每一块的最小值。

习题五 - U147877 分块问题 8

查询区间内多少个数为 c,并把区间改为 c

经过我和几个初二学长的探讨,本题珂朵莉树解法的复杂度没有问题。

但是毕竟是练习分块,我还是老老实实地写了分块。

大致想法就是,对每一块记录区间赋值标记,优先级高于每个数的值,初始区间赋值标记设置一个特殊值表示不存在。其他的就比较基础了。

习题六 - P4168 [Violet]蒲公英

这题貌似被我咕了好久,到现在都没做。。。

先放在这里吧,有时间做完再补上(((

挑战 - 学分块怎么能不做 Ynoi 呢

写了这么多字,这部分先咕着吧,以后有时间再写(

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容