某些想法

发布时间 2023-10-17 09:56:35作者: 瑞恩尼lower

1.转换遍历对象与改变求和顺序:
对于某些需要枚举序列元素来获取信息的题目,当n很大以至于O(n)也超时的时候,我们可以考虑枚举序列中数的值,把枚举对象转换为值域,枚举值域的贡献。

Effects of Anti Pimples这题。

很显然有一个做法是枚举每个数的所有所在集合然后找出最大值来算,时间复杂度会爆炸。但实际上,对于该题可以通过直接枚举以a[i]为黑染色后面最远加入a[j]时集合的最大值来减少无效遍历,只需考虑一个数若在序列中是集合中的最小最大值,显然只有1个集合里有它,而后可以推出对于第i个最大值,显然有 2 ^ (i-1)个集合里其为最大值。只算它作为最大值的贡献,便可最多在O(nlogn)的时间复杂度内解决问题。

还如该题:


该题n,k<=3e5,ai <= 1e6
该题如果是按满足i - j >= k的序列下标进行遍历,然后判断是否是最大值需要O(n ^ 2)的时间,但或许可以换一个角度,我们提前预处理出来某个数或某个数的倍数在序列中最靠左的位置和最靠右的位置,然后枚举值域中的每个数,看两个预处理出来的位置之差是否大于等于k,我们只是转换了条件,因为当某个数对gcd最大的时候,显然是两者相等的情况,当然这种条件比较苛刻,它的倍数与它的gcd也是它本身,从大到小枚举这个数,第一个合法的一定是最大值。相当于我们先满足gcd(a[i],a[j])最大这个条件,再判断下标之差是否大于等于k。
我们再思考一下为什么第一种方法会O(n ^ 2),这是因为对于一个已经在序列中出现过的数,我们后面再枚举到的时候,其实就没必要向后枚举了,因为若前面那个数不满足条件,此时枚举的数在后面,更没有必要枚举。这是正着来的,我们再反着来一遍,便能得出所有合法情况。但此时依然不是很优,因为我们这样枚举依然会枚举到很多无效的数,它们对答案并无影响,我们只考虑对于一个数比它大且为它的倍数的数对它的影响即可。
好了,由第一种方法的局限性,我们考虑要提前预处理出来一个数前面与后面比它大且为它的倍数的数的位置,并且对于一个数我们只用处理一次,后面再出现重复的也没必要了。既然如此,为何不构造一个每个数只出现一次的序列呢,更简单一些,为什么不直接枚举这里面每个数的值呢。由此第二种方法自然推出。

还有一个经典问题。

求树上所有点对的距离之和,当然,我们可以直接两个for,for循环,然后用lca算距离,O(n ^ 2 log n)当然可以解决,但我们可以考虑点对距离之和,实际上是由两点之间的边组成的,每条边的权值一定,该边做的贡献显然是将他切断后,所形成的两个区域的点数的乘积乘上边的权值,我们可以先dfs出每个节点的子树内的节点个数num[u],然后num[u] * (n - num[u]) * w即可,最后求和,即为所求。也不只是所有点对之间的距离,凡是边所能维护的某些权值均可。也不是非要所有点对,也可以存在一些特殊点,这时候,我们考虑dfs出一个子树内所有的特殊点个数,然后利用num[u] * (sum - num[u]) * w即可。

[CSP-S2019 江西] 和积和

该题的形式比较复杂,纯暴力显然是O(n ^ 4),用前缀和预处理的话能达到O(n ^ 2)。
这时候我们不妨考虑,该题所给式子的意义是什么。在[1,n]中的所有子区间中区间内b的和与a的和的乘积,我们的暴力相当于是为了先满足区间这个条件,然后算b和a对于该区间的贡献,不妨换一个角度。我们直接考虑b和a对于所有区间的贡献,即
将原式换成 ∑b[i] * ∑a[j] * q[i],即考虑包括b[i]与a[j]的区间个数有多少个。当i < j 时,显然此时区间的左端点有i个取值点(1 ~ i),区间的右端点有n - j + 1个取值点(j ~ n),那么此时对答案的贡献就是 ∑b[i] * i * ∑a[j] * (n - j + 1),i > j 时也易类比推出,得到这两个式子后,我们发现,∑a[j] * (n - j + 1) 与 ∑a[j] * j 都是可以预处理出来的,于是O(n)即可解决。这题我们一开始思考式子的实际含义,然后转换遍历对象,实际上是一种改变求和顺序的想法。
改变求和顺序也有一个经典的应用。

莫比乌斯反演

这个经典式子:
∑∑[gcd(i,j) == 1]
用莫比乌斯反演后可以转变为∑∑∑μ(d),其中第三个∑,d的条件是为gcd(i,j)的约数,此时可以抽象成一个表格,有n行m列,对于i行j列的这个格子,它的贡献是∑μ(d),d|gcd(i,j),那我们可以转换一下遍历对象,考虑对于一个d,它对多少个表格做了贡献,即它是多少个表格行数列数gcd的约数,那我们可以把原式改成这样 ∑μ(d)∑∑[ d | i 且 d | j ],这实际上是改变了求和顺序,而后面那两个∑,很显然是down[n/d]*down[m/d] (down为向下取整)。
由此我们多少可以看出,求和顺序的改变本质显现的是改变遍历对象,而改变遍历对象在数学式子上的体现便是求和顺序的改变。