设x所在块的编号为BID[x]。
把所有询问[L,R]按照(BID[L],R)的二元组去排序。
从第一个询问开始,每次暴力移动询问区间。
暴力移动的过程中,将L和R每次移动一个,加入或者删除区间的端点。
因为每次只会加入或者删除一个,而且空间只需要维护一个区间的信息,所以可以完成大多数区间询问。
当信息不满足结合律时,不能用树形结构,此时莫队算法就有用了。
分块大小为 n√ ,总时间复杂度 O(nn√)
小优化:当下一个询问的R比当前询问的R小,说明已经遍历了一次了。这时不需要一个一个地将R往左移动,直接从下一个询问开始即可。
考虑加入修改信息。
将询问设为[T,L,R],T是第T次修改后的意思。
那么按照(BID[T],BID[L],R)的三元组进行排序,重复莫队的过程即可。
考虑修改,因为我们会加入和取消某一次修改,所以每一次修改都要记录修改前的和修改后的信息。
如果修改在[L,R]之间,就要计算贡献。
如果修改不在[L,R]之间,直接在外面修改。
块的大小为 n23 ,总时间复杂度 O(n53) 。是很极限的,易卡。
区间的分块的方式为树上分块,通常按高度或者DFS序分块即可。
修改次数按照普通的方法即可。
排序时,R要按照DFS序排序。
移动区间的方式和在序列上的是有区别的。
我们给在询问内的节点打上标记,以判断每一个节点在不在询问内。
这样,在树上移动就相当于给每个节点取反,在询问内的就取出,不在的就加入。
设原来的询问为x、y,后来的询问为x1,y1。
将x到x1的路径取反,y到y1的路径取反。
取反的过程中,不取反LCA。
这时,再将x和y的LCA取反,以及x1和y1的LCA取反。
求LCA的过程在莫队之外,所以不增加时间复杂度。
对于大部分题目来说,只有插入都会比有插入和删除要简单。
普通的莫队是有删除的,当左端移动的时候,就会发生删除。
但是,是有办法不用删除的。
考虑到:左端只在一个 n√ 的块里移动,如果我们把询问分成两个部分:
左端所在块内 , 左端所在块后到右端。
那么,我们就可以是两部分都只有插入。
左端所在块大小为 n√ ,我们暴力预处理后缀和即可。
右端本来就只有插入。
这样,莫队算法就可以只维护插入了。
将询问按R排序。
从左到右扫描。
对于每一个 Ai,考虑询问的 L 在哪些位置时,Ai 对该询问有贡献。
修改 Ai 有贡献的 L 位置的区间。
对于每一个 R 在当前位置的,询问 [L,R] 区间。
在对L计算贡献的基础上,如果询问为:[L,R] 区间中的最优子区间,那么记录历史最优值。
即:当 R 移动时,可能会改变最优值。
那么 R 在 [L,R] 之间时的历史最优值,就是 [L,R] 区间中最优的子区间。
同对 L 计算贡献,但是每次是删除 AL,如果删除比加入好做的话可按L排序。
对 R 排序,每次取消掉 [1,L-1] 的影响,余下的就是 [L,R] 。
颜色:区分不同种类的信息
例如:区间颜色数,区间只出现一次的颜色数,区间出现多次的颜色数etc。
当操作和记录的信息差别很大的时候,怎样才可以不打两次线段树(平衡树)?
先看每一个点实际要记录什么,实际上除了树结构本身外,只有标记和数据两个部分。
标记很少不下传的,所以默认标记要下传。
操作方式默认为基本段改+段查询,简化为打标记+求一段的数据。
(翻转不考虑)
再考虑标记和数据分别会发生什么?
标记:打上标记时初始化,下传时将两个标记合并,将标记应用到数据上。
数据:初始化,两个数据合并,应用标记。
其他东西都是一样的。
然后我们就可以把标记和数据封装一下。
初始化写入构造函数,合并重定义一下+号,应用标记给数据写一个Apply函数。
要求数据和标记都是可以合并的,而且满足结合律。
然后就可以把线段树(平衡树)写成模板了。
注意点:
1、默认数据不满足交换律,没有单位元,所以询问要写成三段式。
2、平衡树的数据合并方式是:Data[lef]+Data[self]+Data[rig],所以每一个点实际要保存两个数据:自己和自己表示的一段。
3、翻转(将第一个和最后一个交换,第二个和倒数第二个交换……)另外写。
4、数据和标记是对应的。
首先,要把匹配的内容给Hash了,才能放进数据结构里。
简单来说,重点还是Hash。