$MoTeam$
莫队是一种暴力算法,用于求解一类区间计数问题。
普通莫队
给你一个有 $n$ 个元素的序列, $m$ 个询问,每个询问有 $l,\ r$ ,询问 $l - r$ 这段区间不同数字的出现个数。
先考虑暴力,每次 $l - r$ 循环一次统计答案,复杂度 $\Theta (nm)$ 。
然后考虑优化。
发现可以把每次区间的左右端点记录下来,由已知的信息推出未知的信息。
但复杂度上界并没有改变,还是 $\Theta (nm)$ 。
考虑离线把所有左右端点记录下来,以左端点为第一关键字,右端点为第二关键字排序,如此使得移动次数最少。
还有优化就是按照奇偶性排序,据说快一倍。
给出一种方式:
1 | inline bool cmp ( MoTeam a, Moteam b ) { |
然后就暴力,根据题目要求更改 $add$ 与 $del$ 函数即可。
给出实现:
1 | while ( R < Q[i].r ) add ( a[++R] ); |
带修莫队
带修莫队顾名思义,就是有单点修改的莫队,此时我们考虑再记一个 $Time$ 表示距离本次查询最近的修改,如果我们莫队改的少了就再改几次,改的多了就改回去,以此来避免修改操作对答案造成的影响。
同时我们多记一个 $Update$ 数组,来存储修改操作,再在问题数组中也记一个 $Time$ 记录当前查询发生在哪次修改之后,然后回答时把序列改成此次修改之后的序列再操作。
其他的就与莫队本身无区别了。
下面给出一道例题的代码
$Code:$
1 |
|
树上莫队
其实就是把莫队求的东西换了一换,核心代码不变。
用于解决求 $x \to y$ 的路径上的问题。
我们考虑有什么东西可以把树上的问题转化到序列上。
这时候就要用到一种东西,欧拉序。
就是访问一个点 $u$ ,把他加进序列,然后遍历他的子树,然后再把他加进序列。
我们维护一个 $st$ 数组和一个 $ed$ 数组, $st_i$ 表示刚访问到点 $i$ 加入序列的时间, $ed_i$ 表示回溯时访问到点 $i$ 的时间。
对于一对 $x,\ y$ 我们钦定 $st_x < st_y$ ,然后分类讨论。
$1.$ $LCA(x, y) = x$ 。
此时 $x$ 是 $y$ 的祖先,即 $x$ 与 $y$ 在同一条链上,所以只需要统计出现过只一次的点即可。
$2.$ $LCA (x, y) \neq x$
此时 $x,\ y$ 位于不同的子树内,那么我们只需要统计 $ed_x \to st_y$ 这条路径上的点。
注意还要特判 $LCA$ ,因为之前统计的时候并没有算进去,手摸一下就知道了。
就没了,然后就是代码实现问题了。
给出例题完整代码
$Code:$
1 |
|
树上带修莫队
就是把树上莫队和带修莫队并起来。
听起来似乎非常高端,代码其实就是以上两份代码的结合。
我们直接进入一道例题分析一波。
这道题要求 $V_i \times \sum_{j = 1}^{cnt[i]} W_j$ ,支持修改,游览点关系是一棵树。
即第 $i$ 种糖果的美味指数乘上所有新奇指数的和。
那么我们就知道 $Add$ 函数怎么写了:
1 | inline void Add ( ll x ) { |
就是模拟时间流逝,如果曾经计入了答案,就把他踢掉,没计入就把他计入,然后根据乘法分配律搞一搞即可。
然后也没什么了,就把上面两份代码并起来魔改一下就好了。
具体看注释。
$Code:$
1 |
|