分块——从入门到放弃
前言
博主因为是蒟蒻,所以很多时候想不出线段树标记下传,所以就写分块暴力下传。为了造福人类,就准备分享一下写分块的经验。
温馨提示
本文不赘述基本写法,只讲述思路。
基础分块
例题 1
先化简题目意思:
维护一个序列,维护区间加和区间大于 \(x\) 的数的数量。
首先分块后问题拆成了块内大于 \(x\) 的数的数量之和与散块大于 \(x\) 的数的数量之和,散块直接暴力查询就可以了。
块内查询怎么搞?我们知道,假设数据有序可以直接二分。
我们又注意到,块内加不影响数据有序性。
所以答案就呼之欲出了。
块内二分,散块排序重构。
例题 2
考虑分块。
显然需要维护开着的灯的数量。
散块可以暴力搞。
整块打标记可以将开着的灯的数量变成块长减去开着的灯的数量。
那么就做完了。
维护置换
例题 1
考虑到 \(\operatorname{popcount}(x)\) 操作后块内不同的数的数量不超过 \(\log V\)。
因此块内维护一个长度为 \(\log V\) 的置换,散块操作再重构即可。
值域分块
例题 1
这种鬼畜的问题优先考虑莫队。
现在的问题就只剩下一个,维护集合 mex
。
考虑维护一个桶。
那么如果我们可以桶分块,并维护每一块中一共出现了多少种数字,这个显然很容易 \(O(1)\) 维护。
那么在查询的时候,对于某个块如果所有数字都出现过就跳过,否则的话由于 mex
一定在其中,那么就暴力查找即可,复杂度是 \(O(\sqrt n)\) 的。
注意到莫队只在移动过程中对桶修改,而在回答询问时才在桶中查询。
所以总复杂度仍然是 \(O(n \sqrt n)\) 的。
值域并查集
例题 1
首先我们发现在修改操作之后不同数的数量一定不增。
这是一个提示。
那么考虑:令块内最大值为 \(k\) 若 \(k \leq 2 \times x\) 则将大于 \(x\) 的数减去 \(x\),也就是花费 \(k - x\) 的势能让 \(k\) 减去 \(k - x\),否则先打一个减 \(x\) 的标记,再让小于 \(x\) 的数加上 \(x\),即花费 \(x\) 的势能让 \(k\) 减少 \(x\)。
然后考虑怎么维护这个东西,显然可以用一个并查集维护每一个值变成了什么,那么就可以做到 \(O(n \sqrt n)\) 时间的维护了然而空间依然过不去,但是卡空间的技巧之后再说。