一些区间无重和问题

区间无重和问题:

问题描述:

对于一个有序数组 (a[1…n]) ,希望(O(lg n))地查询任意区间 ([l, r]) 中元素之和,且重复元素只加一次。

解决方法:

对于每个点上的元素,我们额外维护一个前向指针 (prev),指向它左方最近一个与之具有相同值的元素。

有了前向指针,对于任一区间([l, r]),显然只需遍历一遍,将所有满足 (prev < l) 的元素累加,即可得到答案。

接下来思考如何优化这个过程,观察答案的式子:

[sum_{prev_i < l} {a_i} ]

我们发现,如果将结点按照 (prev) 关键字排序,那么答案就是一个前缀和,因此我们可以在排序后预处理出前缀和,然后二分查找得到答案。

还有区间查询的问题未解决,自然想到线段树:对树上每一个结点作预处理,将其对应区间内的元素进行排序并记录前缀和;对区间([L, R])查询时,只需对线段树上所有子区间二分查找出 (prev < L) 的前缀和,然后合并答案即可。时间复杂度(O(n lg^2n)),空间复杂度(O(nlg n))

缺点:不支持在线增删


重新考虑这个问题,我们要在线地查询

[sum_{prev_i < L} sum_{L leq i leq R} a_i ]

直接一个树状数组套线段树就可以解决了,写起来甚至比上一个方法简单)

有一些题目,层层剥开后,就发现就是一个无重和/积问题。
另外,将 (a_i) 设置为 (1),答案就是区间中去重元素个数。

例题

例1:codeforces 1422 F
刚才说的第一种方法就是从这学的。。
问题描述:给一个正整数序列,每个元素不超过2*1e5,强制在线查询给定区间的LCM。
分析:在素因数分解后,若干个数的LCM即为各个素因子幂次取max,然而,素数太多了,不能这么干,真的不能吗?

注意到 (n) 至多只有一个大于 (sqrt n) 的素因子,其余素因子都是不超过 (sqrt n) 的,所以我们先对 450 以下的素因子建立线段树维护区间最大幂次,将这些小的素因子提出后,每一位剩下的只能是 1 或者 素数。问题就转化为对一个素数序列动态查询区间LCM了,而素数们的LCM,不就是去重积吗?

例2:codeforces 1436 E
分析:求一个有序数组的所有子段(一段连续的元素)的Mex的Mex,问题看着很吓人,不过我们首先注意到 (1 leq a_i leq n),这说明所有子段的Mex一定位于([1, n + 1])中,所以我们可以令 (x)(1) 遍历到 (n + 1),逐步检验是否存在一个子段的Mex为 (x)

任何一个Mex为 (x) 的子段一定不包含 (x),而在不含 (x) 的条件下,子段当然越大越有可能满足条件,考虑 (x) 会将序列分为若干不含 (x) 的极大子段,只需对这些极大子段逐一验证即可,在 (x) 遍历的过程中,总共会出现 (O(n)) 个极大子段,如何快速地处理每个子段呢?

对不含 (x) 的子段进行验证,其实就是判断它是否含有 (1)(x - 1) 的所有值,我们希望它等价于 “区间元素个数为 (x - 1)(去重) ”,为此,只需要在每次遍历完 (x) 后,再把所有值为 (x) 的元素放入树套树。这样就能 (O(lg n)) 地处理每个极大子段了。

原文地址:https://www.cnblogs.com/komeiji-green/p/13922910.html