数据结构基础模板题
常用技巧:反演,差分,离散化
求区间加,单点查
BIT / SGT / BST 都是 \(O(log)\) ~ \(O(log)\) 的,一般用 BIT 差分一下就够了。
分块 \(O(\sqrt n)\) ~ \(O(1)\) ,有些时候可以用来平衡复杂度。
一个带权序列,在区间 [l, r] 中,查询第一个大于等于 x 的数和第一个小于等于 x 的数。(类前驱后继)
sol1 : 莫队 + set(劣)
sol2 : 可持久化至于线段树维护max,在线段树上二分(单 log 优秀做法)
具体来说:
区间 [1, r] 查询最大值
sol : 值域主席树,下标是权值,维护对应权值区间是否出现了数,定位到版本 r,线段树二分这个区间是否出现了一个数。
区间 [l, r] 查询最大值
sol : 定位到版本 r,线段树二分这个权值区间是否出现了一个下表大于 l 的数。改为维护区间 max,线段树二分这个区间 max 是否大于 l。
区间 [l, r] 查询 x 前驱
即在查询权值区间 [1, x] 的最大值。
先把 [1, x] 拆成线段树上对应区间的节点,然后从右到左枚举这些节点,找到第一个区间 max 大于 l 的节点,然后就没有值域的限制了,可以在用上一个问题的 sol。注意这里是一个 log
查后继
相当于把原序列每个点的权值改为 inf - val[i] + 1,其他不变(!!!),然后就变成了查前驱。(这是一种简便写法,也可以再写一个函数,但是这样更简便)
易错点
- 值域大的话要离散化
 - 主席树的那一系列易错点
 - 注意 x 的前驱包不包含 “等于 x”
 - 线段树 query 可能需要特判掉 qL > qR 的极端情况
 - 值域的上界容易错写成 n 之类的东西
 
每个节点有一个数,询问在某颗子树内是否出现过 x
离线 : 线段树合并 / dsu on tree (dsu 是 “树上莫队”的绝对上位)
在线 : 可持久化线段树合并(约两倍空间)
\(\min_{l\le i \le r}\{f(i), g(i)\}\),f 单调
双 log (精细实现可能单 log) 解法 :
- 二分答案 ans
找到 f(i) >= ans 的区间 (这里利用了 f 单调性,容易发现只有一个连续区间) - 用数据结构查询 g 在该区间的 max 是否大于 ans (这里用 st 表之类的有可能可以降 log)
 
一个序列一颗树,找出在子树并且在区间(是否出现/出现次数)颜色 c
发现支配性质,对于每种颜色,在子树中统计每一个数的前驱后继,保证占用的区间最小,有支配性质。用 dsu ,找出 nlogn 个支配区间来统计答案,统计答案可以用扫描线。
每个区间询问只需要找到区间中一个数来统计答案就行了,所有数的和为 S ,用 x 处理一次的复杂度是 O(x (r - l + 1) ) 的
倍增分治。
例题:[P5576 CmdOI2019] 口头禅 - 洛谷
发现每次取小的数,覆盖的区间太少,取位置中间的数,每次复杂度太高。
倍增地来搞,每次取值域在 \([2^k,2^{k+1})\) 的数,用完以后,可以把序列划分成几个更小的块,这些块内的数都大于 \(2^{k+1}\)。
每次这样取\([2^k,2^{k+1})\) 的数的时候也是取这些数中间的数,然后分治下去搞。
分析复杂度,一层会处理 \(O({S\over2^{k}}logn)\) 次,每次复杂度是 \(O(2^{k+1})\) 的,会分 \(log V\) 此,所以总复杂度大约是两个 log
区间众数
在线做法:
先离散化,发现区间众数不好合并,然后直接莫队的话,删除需要带 log。考虑分块,预处理出每两个块之间的答案,处理出每种颜色在以每个块右端点为界的前缀的出现次数,这个可以做到 \(O(n\sqrt n)\) 预处理。
对于查询,答案要么是整块的答案,要么是散块中出现的数。可以通过遍历散块来求出这些数出现次数(差分出这些数在整块中出现次数)来比大小。
弱化1:序列 a 单调
可以做到 log 预处理,O(1) 查询。不知道有没有 O(1) 预处理的做法,反正我不会
找出每个值相同的连续区间,然后给每个位置标记一下这个数所处区间的左右端点和区间长度,称区间长度为该点权值。维护一个区间权值 max 的 st 表。
查询直接查询左右端点所在块和中间剩下的块的答案。
弱化2:查询区间出现次数大于区间长度一半的数。
用主席树可做到一个 log。
在值域上维护可持久化线段树(主席树),判断值域上值域在 [l,mid] 的数的出现次数之和是否大于区间长度的一半,否者看 [mid+1,r] 。可以直接这么做是由于保证了是过半众数。
思考:为什么这个做法只能做过半众数?因为主席树维护的信息需要通过在 r 和 l-1 两个版本上做差分得来的,取 max 的操作不被支持。只能维护区间和。
过半众数如果待修,还可以用摩尔根投票法的合并来做,对于判断找到的数是否大于区间长度的一般,可以对每种颜色开个平衡树来维护一下这个数在 [l,r] 上出现次数。例题:P3765 总统选举 - 洛谷。
过半众数似乎可以用随机化乱搞。。。
莫队
用于解决统计二元组个数的问题。只要能解决在一个集合中加入一个数和删除一个数时维护变化量即可使用莫队。
二次离线莫队
用处:解决了加入删除数维护变化量复杂度过高的问题。
条件:
- 可差分性,设 f(l,r,x) 表示 x 对 l,r 的贡献,则 f(l,r,x) = f(1,r,x) - f(1,l-1,x)
 - 通过预处理前缀,可以快速算出 x 在一个前缀 (1,l) 的值。反演可能有奇效。
 
例题:P4887 【模板】莫队二次离线(第十四分块(前体)) - 洛谷。
珂朵莉给了你一个序列 \(a\),每次查询给一个区间 \([l,r]\),查询 \(l \leq i< j \leq r\),且 \(a_i \oplus a_j\) 的二进制表示下有 \(k\) 个 \(1\) 的二元组 \((i,j)\) 的个数。\(\oplus\) 是指按位异或。\(0≤a_i,k<16384\)
每次加入最大有一个 \(17\choose 8\) 的复杂度。(大概?)
假设现在莫队需要把维护的区间 [l,r] 拓展到 [l,R] (r < R),加入的权值是 \(\sum_{r+1\le x\le R} f(l,x-1,x) = \sum_{r+1\le x\le R} f(1,x-1,x)-f(1,l,x)\).
f(1,x-1,x) 直接预处理,现在就是要求 \(\sum_{r+1\le x\le R} f(1,l,x)\),设这个为 \(F(l,r+1,R)\),把这个询问挂到 l 上
扫描线把每个前缀维护出来,然后查询 \([r+1,R]\) 每一个数的贡献,这个查询要支持 \(O(1)\)。
在线莫队(?) Trick
本质是分块。先预处理出任意两个块之间的答案,然后每次询问就只暴力加入散块的数。
树上莫队
用欧拉序,变成序列问题
带修莫队
调块长
回滚莫队
解决删除(或加入)比较困难的问题。
左右端点同区间的暴力。
同左端点区间的询问的右端点从小到大排序。(不能奇偶分类!)
初始区间 l 是区间右端点+1,r 是区间右端点。
这样左端点暴力加,加完了就直接回溯到 l = R+1 的版本,右端点不用回溯(毕竟排过序)

                
            
        
浙公网安备 33010602011771号