zoukankan      html  css  js  c++  java
  • 差分前缀和及其扩展与优化

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

    一维一阶前缀和

    对于一个数组 (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)


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

  • 相关阅读:
    小白学 Python 数据分析(21):pyecharts 好玩的图表(系列终篇)
    小白学 Python 数据分析(20):pyecharts 概述
    小白学 Python 数据分析(19):Matplotlib(四)常用图表(下)
    小白学 Python 数据分析(18):Matplotlib(三)常用图表(上)
    在 ASP.NET Core 程序启动前运行你的代码
    在 ASP.NET Core 项目中使用 MediatR 实现中介者模式
    在 ASP.NET Core 项目中使用 AutoMapper 进行实体映射
    [Vue 牛刀小试]:第十七章
    或许是你应该了解的一些 ASP.NET Core Web API 使用小技巧
    [Vue 牛刀小试]:第十六章
  • 原文地址:https://www.cnblogs.com/zythonc/p/14415401.html
Copyright © 2011-2022 走看看