莫队这个根号我觉得是比分块灵活的,就在于他有许多的方式
莫队的先决条件,在我看来就是两个
1:可以从区间 [l,r] [ l , r ] 快速转移到 [l,r+1][l,r−1][l+1,r][l−1,r] [ l , r + 1 ] [ l , r − 1 ] [ l + 1 , r ] [ l − 1 , r ]
2:时间允许 O(n∗sqrt(n)) O ( n ∗ s q r t ( n ) )
简单莫队 。
因为我们可以发现我们从 [l,r] [ l , r ] 转移到 [l1,r1] [ l 1 , r 1 ] 的次数是 abs(l1−l)+abs(r1−r) a b s ( l 1 − l ) + a b s ( r 1 − r ) ,像极了曼哈顿距离,所以我们把每一个关于区间的询问抽象成二维平面上的点,然后用根号为块的大小进行询问的划分(证明网上一大堆),因为每次改变左、右端点是 O(1) O ( 1 ) 这样我们就可以简单期望效率是 O(n∗sqrt(n)) O ( n ∗ s q r t ( n ) )
板子题也是一大堆,这里不列举
不是那么简单的莫队。
考虑当我们的操作加入了修改?我们应该怎么办?
修改操作是不和谐的,因为修改相当于是在本来的二维平面上强行有添加了一个维度,所以我们只有给每个询问或者秀改操作加上时间标记,当我们执行修改的事就就可以跳到当前我们的时间,这样我们就可以进行维护了,但是我们发现在这种情况下块的大小不是最优的,所以我们把块的大小调整为 n2/3 n 2 / 3 。
这里给出复杂度为 n5/3 n 5 / 3 的简单证明:
当左右端点所在块不变时,询问是按时间排序的,因此询问的时间不降,所以时间指针只会向后移动 n n 步,而左右两端点改变时时间指针最坏情况下会一次向前移动 n n 步。而由于左右两端点所在块的种类组合起来只有 n2/3 n 2 / 3 种,且每种组合中,时间指针只会向后移动 n n 步;取值改变也只有 n2/3 n 2 / 3 次,时间指针每次向前移动 n n 步,时间指针的总复杂度即为 n5/3 n 5 / 3 。
左右端点所在块不变时,右指针一次移动最多 n2/3 n 2 / 3 步,由于有 n n 次询问,所以这里的复杂度不超过 n5/3 n 5 / 3 。而当左端点所在块不变,右端点所在块改变时,右指针就不会往回移动到前一个块中,因此左端点所在块不变,由于右端点所在块改变所带来的的移动的复杂度不超过 n n ,而总共有 n1/3 n 1 / 3 种左端点所在块取值,所以这里的复杂度不超过 n4/3 n 4 / 3 ,而当左端点所在块改变时,右指针最坏是一次移动 n n 步,与之前类似,总共有 n1/3 n 1 / 3 种左端点所在块取值,所以这里的复杂度不超过 n4/3 n 4 / 3 。右指针总时间复杂度为 n5/3 n 5 / 3 。
左端点所在块不变时,左指针一次移动最多 n2/3 n 2 / 3 步,由于有 n n 次询问,所以这里的复杂度不超过 n5/3 n 5 / 3 。当左端点所在块改变时,由于排序是按左端点所在块为第一关键字,所以之后左指针就不会移动回前一个块,因此这种情况下的左指针移动带来的总复杂度不超过 n n ,左指针总时间复杂度也为 n5/3 n 5 / 3
综上,这个拓展的做法的总时间复杂度即为 n5/3 n 5 / 3
模板:BZOJ2120
建成树的莫队。
有时我们所处理的问题并不为序列上的区间问题,而是树上的路径。那么实际上我们也可以把莫队算法拓展到树上,即树上莫队。
核心思想依旧没变,因此我们需要考虑的仍是如何将询问排序。而常用方法则是将树上的点标号,使其变为序列,然后用普通的莫队算法解决。
解决时唯一不同的则是原来序列上用的是左右指针移动,现在树上这个方法不太好用,因为左边或右边可能都与当前位置节点不相邻,并不是我们需要维护的路径上的东西。
所以我们应该怎么办?
观察后可以发现:
而从 (u1,v1) ( u 1 , v 1 ) 转移到 (u2,v2) ( u 2 , v 2 ) 我们需要把 (u1,u2) ( u 1 , u 2 ) 路径上除了 lca(u1,u2) l c a ( u 1 , u 2 ) 之外的点在答案中的状态取反,把 (v1,v2) ( v 1 , v 2 ) 路径上除了 lca(v1,v2) l c a ( v 1 , v 2 ) 之外的点在答案中的状态取反。通过画图或者简单推导我们能现在,这么做后,剩下的在答案中的点,都在 (u2,v2) ( u 2 , v 2 ) 它们之间的除去lca之外的路径上。
复杂度证明真不会,可以参见vfk博客中的证明。
对点进行标号的方法一般有两种:
1.我们DFS 这棵树,同时记录下它们的入栈出栈序,对这个序列进行分块
2.对树分块(对树进行DFS,每个点出栈后将其放进队列,当队列里的点超过B 个时将这些点分为一块)
个人偏爱第二种
模板题BZOJ3052,树上带修莫队,代码丢失了。。。。
//qwq
科技莫队。
跟yyf大佬学的。dalao blog
在莫队转移过程中,如果我们发现删除操作不好进行,那么我们就可以用回滚莫队技巧来回避掉删除操作
我们在左端点 blocki b l o c k i 中的所有讯问,容易发现右端点是单调不减的,所以我们可以每次都把左端点从 blocki b l o c k i 的右端点向前跑,然后右端点直接向后跑就可以了。注意在这里先跑右端点记录下左端点在 blocki b l o c k i 的右端点的答案方便后面的计算。
然后考虑特殊情况,如果一个询问左右端点在同一区间,暴力跑过去就好了,时间效率上限是块的大小。如果一个询问左端点所在的块不等于上一个询问的左端点所在块,即进入了一个新的块,我们直接从这个询问的l跑到r记录答案就好了,单次 O(n) O ( n ) ,因为块数为 sqrt(n) s q r t ( n ) ,所以时间上线还是 O(nsqrt(n)) O ( n s q r t ( n ) )
模板题直接看今天的考试题好了。。
或者BZOJ4241也行