差分前缀和及其扩展与优化

很没用的总结,如果您发现了错误,欢迎指正!!

一维一阶前缀和

对于一个数组 (A),设其前缀和数组为 (sum)

(sum_i) 的意义是 (sumlimits_{k=1}^iA_k),即区间 ([1,i]) 所有数的和

显然对于不带修改的区间求和,我们可以用前缀和数组 (O(1)) 查询

(sumlimits_{i=l}^rA_i=sum_{r}-sum_{l-1})

构造

构造前缀和数组:(sum_i=sum_{i-1}+A_i)

一维一阶差分

对于一个数组 (A),设其差分数组为 (dif)

(dif_i) 的意义是 (A_i-A_{i-1}),即相邻两个数的差

对于单点或区间加减法,如果最后只是求修改完毕后的数列,那么我们可以 (O(1)) 修改,最后 (O(n)) 还原

([l,r]+addRightarrow dif_l+add,dif_{r+1}-add)

最后 (A_i=sumlimits_{k=1}^idif_k)

构造

构造差分数组:(dif_i=A_i-A_{i-1})

高维一阶前缀和

对于一个高维数组 (A),设其前缀和数组为 (sum)

(sum_{i_1,cdots,i_k}) 的意义是区间 ((1,cdots,1)sim(i_1,cdots,i_k)) 内所有数的和

利用容斥原理,即可对不带修改的高维区间求和询问 (O(1)) 回答

构造

(k) 维前缀和即利用 (k) 重循环直接加法计算

(sum_{i_1,cdots,i_k}=sum_{i_1,cdots,i_{k-1}}+A_{i_1,cdots,i_k})

高维一阶差分

对于一个高维数组 (A),设其差分数组为 (dif)

对于一个 (k) 维区间加法,我们要对其区间开端 (+1),区间末端 (-1)

例如 (2) 维区间加法 ((x,y)sim(u,w))

显然 (dif_{x,y}+1,dif_{u+1,y}-1,dif_{x,w+1}-1,dif_{u+1,w+1}+1)

推广到一般即为高维的情况

构造

一维高阶前缀和

高阶前缀和具有组合意义

对于一个数组 (A),设其 (k) 阶前缀和数组为 (sum_k)

其一阶前缀和 (sum_{1,i}=sum_{1,i-1}+A_i)

对数组 (sum_{k-1}) 再做前缀和,得到的前缀和数组即为其 (k) 阶前缀和数组 (sum_k)

构造

构造一维高阶前缀和数组:

[egin{cases} sum_{k,i}=sum_{k,i-1}+sum_{k-1,i}&(k>1) \ sum_{k,i}=sum_{k,i-1}+A_i&(k=1) end{cases} ]

一维高阶差分

高阶差分具有组合意义

对于一个数组 (A),设其 (k) 阶差分数组为 (dif_k)

其一阶差分 (dif_i=A_i-A_{i-1})

对数组 (dif_{k-1}) 再做差分,得到的差分数组即为其 (k) 阶差分数组 (dif_k)

构造

[egin{cases} dif_{k,i}=dif_{k-1,i}-dif_{k-1,i-1}&(k>1) \ dif_{k,i}=A_i-A_{i-1}&(k=1) end{cases} ]

生成函数优化

本处讨论一维高阶的情况

设序列 (a) 的普通生成函数 (f(x))(sumlimits^{infty}_{n=0}a_nx^n)(g(x)=sumlimits^infty_{n=0}b_nx^n) 是另一个新的数列 ({b_n}^infty_{n=0}) 的生成函数

高阶前缀和

首先写出前缀和的计算方法:(sum_i=sumlimits_{k=1}^ia_k)

熟知 (f(x)g(x)) 是数列 (left{sumlimits^n_{k=0}a_kb_{n-k} ight}_{n=0}^infty) 的生成函数 (性质1)

(b) 的每一项 (b_i) 都等于 (1) 时,(dfrac{f(x)}{1-x}) 即为其一阶前缀和

同理,二阶前缀和是在一阶前缀和的基础上得到的,(k) 阶前缀和是在 (k-1) 阶前缀和的基础上得到的,我们用 (dfrac{1}{1-x}) 不断相乘即可

所以 (k) 阶前缀和即为 (f imesdfrac{1}{(1-x)^k})

根据上文性质1,(dfrac{1}{(1-x)^k}) 即为 (left{sumlimits_{n_1+n_2+dots+n_k=n}1 ight}_{n=0}^infty) 的OGF

考虑组合意义,即 (left{dbinom{n+k-1}{n} ight}_{n=0}^infty) 的OGF

递推组合数:

(dbinom{i+k-1}{i}=dbinom{i+k-2}{i-1}dfrac{k+i-1}{i})

然后跟 (f) NTT卷一下就可以

高阶差分

首先写出差分的计算方法:(dif_i=a_i-a_{i-1})

依然考虑性质1,如果 (b_0=1,b_1=-1,b_i=0;(i>1))

此时 (f imes g=sumlimits_{n=0}^inftyleft(sumlimits^n_{k=0}a_kb_{n-k} ight)x^n=sumlimits_{n=0}^infty(a_n-a_{n-1})x^n)

显然我们依然能将差分数组的计算方法写成生成函数相乘的形式

此时数列 (b) 即为 (1-x)

所以 (k) 阶差分即为 (f imes(1-x)^k)

对于 ((1-x)^k) 直接二项式定理拆开

((1-x)^k) 的每项系数是 (sumlimits_{i=0}^k(-1)^idbinom{k}{i})

递推组合数:

(dbinom{k}{i}=dbinom{k}{i-1}dfrac{k-i+1}{i})

然后拿 (f) NTT卷一下就可以

是不是非常非常简单?

放放代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 1000001
#define INF 1100000000
#define Kafuu return
#define Chino 0
#define fx(l,n) inline l n
#define set(l,n,ty,len) memset(l,n,sizeof(ty)*len)
#define cpy(f,t,ty,len) memcpy(t,f,sizeof(ty)*len)
#define R register int
#define int long long
using namespace std;
const int mod=1004535809,pr=3;
int n,k,t,ori[N],invp,invx,x=1,br[N],prp[N],binom[N],T[N];
fx(int,gi)(){
	char C=getchar();long long n=0,f=1;
	while(C<'0'||C>'9'){
		if(C=='-') f=-f;
		C=getchar();
	}
	while(C>='0'&&C<='9') ((n*=10)+=C-'0')%=mod,C=getchar();
	return (n*f)%mod;
}
fx(int,pow)(int a,int b=mod-2){
	int sum=1;
	while(b){
		if(b&1) (sum*=a)%=mod;
		(a*=a)%=mod;
		b>>=1;
	}
	return sum;
}
fx(void,NTT)(int *f,short r){
	R len,hl,exp,uni,st,i;
	for(i=0;i<x;i++) if(i<br[i]) swap(f[i],f[br[i]]);
	for(len=2,hl=1;len<=x;hl=len,len<<=1){
		exp=pow(r==1?pr:invp,(mod-1)/len);
		for(i=1;i<hl;i++) prp[i]=prp[i-1]*exp%mod;
		for(st=0;st<x;st+=len){
			for(i=0;i<hl;i++){
				uni=prp[i]*f[i|st|hl]%mod;
				f[i|st|hl]=(f[i|st]-uni+mod)%mod;
				f[i|st]=(f[i|st]+uni)%mod;
			}
		}
	}
}
signed main(){
	n=gi(),k=gi(),t=gi();
	for(R i=0;i<n;i++) T[i]=ori[i]=gi();
	while(x<=n+n) x<<=1;
	for(R i=0;i<x;i++) br[i]=(br[i>>1]>>1)|((i&1)?x>>1:0);
	invx=pow(x);invp=pow(pr);binom[0]=1;
	if(!t) for(int i=1;i<=n;i++) binom[i]=binom[i-1]*(k+i-1)%mod*pow(i)%mod;
	else for(int i=1;i<=n;i++) binom[i]=-binom[i-1]*(k-i+1)%mod*pow(i)%mod;
	prp[0]=1;NTT(ori,1);NTT(binom,1);
	for(int i=0;i<x;i++) (ori[i]*=binom[i])%=mod;
	NTT(ori,-1);
	for(int i=0;i<n;i++) cout<<ori[i]*invx%mod<<" ";
}

树上前缀和

树上前缀和即是快速回答两点间距离的一种手段,(sum) 数组表示当前点到根结点的距离

于是两点间距离即为 (sum_x+sum_y-sum_{lca})

构造

(sum_{x}=sum_{fa}+v_{fa o x})

树上差分

对点差分

即为路径上的点同加一个值 (val)

对于每个点(p),设 (S) 为其子结点集合,(dif_p=sumlimits_{tin S}dif_t)

注意自下向上有序统计,不要随便找一个点套用上边的式子

构造

(dif_x+1,dif_y+1,dif_{lca}-1,dif_{lcafa}-1)

对边差分

即为路径上的边同加一个值 (val)

(dif_p) 表示边 (fa o p)

统计同对点差分

构造

(dif_x+1,dif_y+1,dif_{lca}-2)


你已经看完啦,相信你已经理解并精通了前缀和与差分,快去研究高维高阶前缀和&高维高阶差分吧!

原文地址:https://www.cnblogs.com/zythonc/p/14415401.html