前缀和与差分

Published: by Creative Commons Licence

  • Tags:

前缀和技术

考虑这样一个问题,给定一个静态数组a1,,an,之后回答q个请求,每个请求给出l,r,要求计算ri=lai

这种静态询问的问题,可以用前缀和技术来加速,具体做法就是我们维护另外一个序列b0,b1,,bn,其中b0为加法0,对于i1,有bi=ij=1ai=bi1+ai。很显然b序列我们可以O(n)时间复杂度内求出。之后对于任意询问ri=lai,有ri=lai=brbl1。因此对于每个请求我们都可以O(1)求解。

当然如果给定序列b,我们也可以很容易还原a,因为ai=bibi1

前缀和技术实际上有点类似于傅里叶变换,将元素转换到另外一个空间中进行求解,从而实现一些操作加速,而另外一些操作减速。这里加速的就是区间和,而减速的就是普通的赋值修改操作(每次修改和赋值的时间复杂度都变成了O(n))。

高维前缀和

现在假设有k维矩形,记f(i1,,ik)为单元(i1,,ik)中的数值。同时记g(i1,,ik)=1j1i1,,1jkikf(i1,,ik)

要算高维前缀和,我们可以定义h(i1,,ik,t)=1jtit,,1jkikf(i1,,it1,jt,,jk)。于是乎g(i1,,ik)=h(i1,,ik,0)

下面我们来推导h,可以发现有:

h(i1,,ik,t)=h(i1,,it1,,ik,t)+h(i1,,it,,ik,t+1)

因此我们可以花总共O(kS)的时间复杂度和空间复杂度求解出结果,其中S是矩形的体积。

题目1:给定n个数值a1,,an,其中0a1,,an<106。记f(x,i)表示数字x的十进制中最低的第i位的数值。记g(x1,,xk)表示一个数字y,其中y的十进制的第i位等于minjf(xj,i)。现在要求对于每个数0x<106,计算g(x)=Sa1,,anf(S)=xsSs,将输出结果模某个素数。

首先我们可以将每个整数看成一个6维向量。很显然这道题要求我们计算高维前缀和,但是如何保证f(S)=s呢。我们可以用容斥解决这个问题,先求出Sa1,,anf(S)xsSs,其中f(S)x表示f(S)的每个维度的数值都大于等于x在该维度的数值。这个很好算。

算出来后,我们用可以用容斥O(26)找出g(x)

总的时间复杂度为O((26+6)106)

题目2:给定n个正整数a1,,an,要求找到最大的一个正整数x,满足给出的正整数至少有一半能整除x。其中1n105,且1ai1018

提供一道题目

首先我们随机选择一个数aiai能整除x的概率至少为12。这意味着x很大概率是ai的因子。

但是我们不能暴力枚举ai的因子,因为ai的因子可能有O(105)个。实际上我们需要遍历所有正整数,记遍历的正整数为y,那么将所有gcd(ai,y)的因子的计数全部增加1。之后找最大的计数超过一半的ai因子。

这个过程非常类似于高维前缀和,如果我们把ai分解为素因子形式ai=pc11pckk,我们用向量表示可以得到(c1,c2,,ck)。这其实就是高维前缀和。根据文章中提到的方法,我们可以O(105×15)来计算高维前缀和,总的时间复杂度为O(105(log21018+15+log2n))

之后我们多随机几次,保证正确率即可。

差分技术

前缀和技术可以用于快速查询区间和但是不支持修改,而差分技术则允许我们快速进行区间更新但是不支持查询。二者是对立的。

考虑在一维数组上做差分,原数组为a,而差分得到的数组为b,满足ai=ij=1bj。考虑如何支持区间[l,r]全部增加v,实际上考虑到b中的变化,可以发现bl需要增加v,而br+1需要减少v。因此一次区间更新就变成了两次单点修改。如果我们要恢复a,则可以用前缀和技术计算b的前缀和即可。

二维数组的差分也类似,这里不多讲。

题目1:给定一个数组a,支持n次操作,每次在区间[l,r]中加上一个等差数列。要求输出最终的a

这个问题非常简单,可以发现等差数列可以由仿射变换得到,即可以表示为kx+b的形式,其中x是元素的下标。我们可以把kb分开来维护。这样问题变成了区间k增加一定值和b增加一定值,这就是普通的差分问题了。

题目2:给定一个长度为n的数组a,处理m次操作,每次选择一个区间[l,r]以及一个整数d,之后元素i加上(il+dd)。要求输出最终的数组。其中1n,m1050d100。答案模素数p

提供一道题目

首先我们可以认为数组初始值为0,只要在最后输出的时候加上ai即可。

之后考虑一次修改的意义,我们可以考虑维护一个(k+1)×n的网格A。我们先简单认为r=n始终成立。之后我们考虑从(d,l)出发,每次只能向下和向右移动。可以发现我们到(0,i)的方案数正好是(il+dd)种方案。现在考虑多个请求,我们实际上可以合并到同一个网格中处理,可以发现这样结果并不会多统计。

现在我们考虑r<n的情况,这时候显然会导致j>r的列多统计了。我们考虑一个请求对于(i,r+1)的影响,此时单元格都多加了(r+1l+(di)di),我们需要减去这个值。最简单的方案是我们在单元格上直接减去这些值,由于我们单元格最终是用来算前缀值的,因此减少(i,r+1)会对(i1,r+1)也会产生影响,因此(i,r+1)单元格实际上要减去的值是(rl+(di)di)

时间复杂度和空间复杂度均为O(100(n+m))