前缀和与差分
前缀和技术
考虑这样一个问题,给定一个静态数组a1,…,an,之后回答q个请求,每个请求给出l,r,要求计算∑ri=lai。
这种静态询问的问题,可以用前缀和技术来加速,具体做法就是我们维护另外一个序列b0,b1,…,bn,其中b0为加法0,对于i≥1,有bi=∑ij=1ai=bi−1+ai。很显然b序列我们可以O(n)时间复杂度内求出。之后对于任意询问∑ri=lai,有∑ri=lai=br−bl−1。因此对于每个请求我们都可以O(1)求解。
当然如果给定序列b,我们也可以很容易还原a,因为ai=bi−bi−1。
前缀和技术实际上有点类似于傅里叶变换,将元素转换到另外一个空间中进行求解,从而实现一些操作加速,而另外一些操作减速。这里加速的就是区间和,而减速的就是普通的赋值修改操作(每次修改和赋值的时间复杂度都变成了O(n))。
高维前缀和
现在假设有k维矩形,记f(i1,…,ik)为单元(i1,…,ik)中的数值。同时记g(i1,…,ik)=∑1≤j1≤i1,…,1≤jk≤ikf(i1,…,ik)。
要算高维前缀和,我们可以定义h(i1,…,ik,t)=∑1≤jt≤it,…,1≤jk≤ikf(i1,…,it−1,jt,…,jk)。于是乎g(i1,…,ik)=h(i1,…,ik,0)。
下面我们来推导h,可以发现有:
h(i1,…,ik,t)=h(i1,…,it−1,…,ik,t)+h(i1,…,it,…,ik,t+1)因此我们可以花总共O(kS)的时间复杂度和空间复杂度求解出结果,其中S是矩形的体积。
题目1:给定n个数值a1,…,an,其中0≤a1,…,an<106。记f(x,i)表示数字x的十进制中最低的第i位的数值。记g(x1,…,xk)表示一个数字y,其中y的十进制的第i位等于minjf(xj,i)。现在要求对于每个数0≤x<106,计算g(x)=∑S⊆a1,…,an∧f(S)=x∑s∈Ss,将输出结果模某个素数。
首先我们可以将每个整数看成一个6维向量。很显然这道题要求我们计算高维前缀和,但是如何保证f(S)=s呢。我们可以用容斥解决这个问题,先求出∑S⊆a1,…,an∧f(S)≥x∑s∈Ss,其中f(S)≥x表示f(S)的每个维度的数值都大于等于x在该维度的数值。这个很好算。
算出来后,我们用可以用容斥O(26)找出g(x)。
总的时间复杂度为O((26+6)⋅106)。
题目2:给定n个正整数a1,…,an,要求找到最大的一个正整数x,满足给出的正整数至少有一半能整除x。其中1≤n≤105,且1≤ai≤1018。
提供一道题目。
首先我们随机选择一个数ai,ai能整除x的概率至少为12。这意味着x很大概率是ai的因子。
但是我们不能暴力枚举ai的因子,因为ai的因子可能有O(105)个。实际上我们需要遍历所有正整数,记遍历的正整数为y,那么将所有gcd(ai,y)的因子的计数全部增加1。之后找最大的计数超过一半的ai因子。
这个过程非常类似于高维前缀和,如果我们把ai分解为素因子形式ai=pc11…pckk,我们用向量表示可以得到(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是元素的下标。我们可以把k和b分开来维护。这样问题变成了区间k增加一定值和b增加一定值,这就是普通的差分问题了。
题目2:给定一个长度为n的数组a,处理m次操作,每次选择一个区间[l,r]以及一个整数d,之后元素i加上(i−l+dd)。要求输出最终的数组。其中1≤n,m≤105,0≤d≤100。答案模素数p。
提供一道题目。
首先我们可以认为数组初始值为0,只要在最后输出的时候加上ai即可。
之后考虑一次修改的意义,我们可以考虑维护一个(k+1)×n的网格A。我们先简单认为r=n始终成立。之后我们考虑从(d,l)出发,每次只能向下和向右移动。可以发现我们到(0,i)的方案数正好是(i−l+dd)种方案。现在考虑多个请求,我们实际上可以合并到同一个网格中处理,可以发现这样结果并不会多统计。
现在我们考虑r<n的情况,这时候显然会导致j>r的列多统计了。我们考虑一个请求对于(i,r+1)的影响,此时单元格都多加了(r+1−l+(d−i)d−i),我们需要减去这个值。最简单的方案是我们在单元格上直接减去这些值,由于我们单元格最终是用来算前缀值的,因此减少(i,r+1)会对(i−1,r+1)也会产生影响,因此(i,r+1)单元格实际上要减去的值是(r−l+(d−i)d−i)。
时间复杂度和空间复杂度均为O(100(n+m))。