单点修改,k 阶前缀和问题通解

发现一个位置 \(x\)\(v\) 对位置 \(y\) 的贡献为 \(\binom{y-x+k-1}{k-1}\times v\)

\(\binom{y-x+k-1}{k-1}\) 即多次前缀和的结论(生成函数,找规律,或组合意义:最后的 \(y\) 固定,前 \(k-1\)​ 次前缀和中每次选一个大于等于当前位置的贡献位置)。

对于 \(k\) 小的情况,比如 \(k=4\),暴力拆开得到 \(\frac16[y^3+(6-3x)y^2+(11-12x+3x^2)y+6-11x+6x^2-x^3]\)

这启示我们对于点 \(y\) 维护一个关于 \(y\)\(k-1\) 次的多项式,直接把 \(x\) 代入算出这 \(k\) 项各自的系数(这些与 \(y\) 无关),维护 \(k\) 个树状数组即可。

这些系数满足:点 \(i\) 的多项式恰好为 \(\binom{x-i+k-1}{k-1}\),这里的 \(i\) 对应上面的 \(x\) 视为常量。

例如 \(k=4,i=5\) 时,\(\binom{x-i+k-1}{k-1}=\binom{x-2}3=\frac16(x-2)(x-3)(x-4)=\frac16(x^3-9x^2+26x-24)\)
\(x=i=5\) 代入 \(\frac16[y^3+(6-3x)y^2+(11-12x+3x^2)y+6-11x+6x^2-x^3]\)\(k\) 项系数一一对应。

时间复杂度:预处理 \(\binom{x-i+k-1}{k-1}\) 的每一项可以 \(O(nk^2)\) 暴力乘,修改查询均为 \(O(qk\log n)\)

经过 MspAint 老师的大力研究,该问题可扩展为:区间加或区间求和,可以加一阶前缀和;区间覆盖,或仅对 \([l,r]\) 进行前缀和,可以使用线段树。

如果直接对线段树的每个节点直接维护 \(1\sim k\) 阶前缀和的值,可以直接求出每一阶前缀和,pushup 复杂度 \(O(k)\),总时间复杂度 \(O(nk^2\log n)\)


P4458 BJOI2018 链上二次求和

代码
#include<bits/stdc++.h>
constexpr int rSiz=1<<21;
char rBuf[rSiz],*p1=rBuf,*p2=rBuf;
#define gc() (p1==p2&&(p2=(p1=rBuf)+fread(rBuf,1,rSiz,stdin),p1==p2)?EOF:*p1++)
template<class T>void rd(T&x){
	char ch=gc();
	for(;ch<'0'||ch>'9';ch=gc());
	for(x=0;ch>='0'&&ch<='9';ch=gc())
		x=(x<<1)+(x<<3)+(ch^48);
}
constexpr int _=2e5+5,mod=1e9+7;
void Add(int &x,int y){if((x+=y)>=mod)x-=mod;}
int pw(int x,int y=mod-2){
	for(int v=1;;y>>=1,x=1ll*x*x%mod){
		if(!y)return v;
		if(y&1)v=1ll*v*x%mod;
	}
}
int n,q,k,a[_];
struct Poly{
	int f[4];
	Poly (){
		for(int i=0;i<k;++i)f[i]=0;
	}
	int& operator [](const int x){return f[x];}
	Poly operator + (Poly y){
		Poly z;
		for(int i=0;i<k;++i)z[i]=(f[i]+y[i])%mod;
		return z;
	}
	void operator += (Poly y){
		for(int i=0;i<k;++i)Add(f[i],y[i]);
	}
	Poly operator - (Poly y){
		Poly z;
		for(int i=0;i<k;++i)z[i]=(f[i]-y[i]+mod)%mod;
		return z;
	}
	Poly operator * (Poly y){
		Poly z=Poly();
		for(int i=0;i<k;++i)for(int j=0;j<=i;++j)
			Add(z[i],1ll*f[j]*y[i-j]%mod);
		return z;
	}
	Poly operator * (int y){
		Poly z=Poly();
		for(int i=0;i<k;++i)z[i]=1ll*f[i]*y%mod;
		return z;
	}
}b[_];
Poly t[_];
void Chg(int x,Poly v){
	for(;x<=n;x+=x&-x)t[x]+=v;
}
int Qry(int x){
	if(x<=0)return 0;
	Poly v=Poly();
	for(int i=x;i;i-=i&-i)v+=t[i];
	int val=0;
	for(int i=0,j=1;i<k;++i){
		Add(val,1ll*v[i]*j%mod);
		j=1ll*j*x%mod;
	}
	// printf("%d %d\n",x,val);
	return val;
}
int sum;
void Upd(int l,int r,int x){
	Add(sum,1ll*(r-l+1)*(n+n-l-r+2)/2%mod*x%mod);
	Chg(l,b[l]*x);
	if(r<n)Chg(r+1,b[r+1]*(mod-x));
}
void Prt(Poly x){
	for(int p=0;p<k;++p){
		int o=6ll*x[p]%mod;
		if(o>1e8)o-=mod;
		printf("%d/6%c",o," \n"[p==k-1]);
	}
}
int Ask(int x){
	return (Qry(n-x-1)-Qry(x-1)+mod)%mod;
}
int main(){
	rd(n),rd(q);k=4;
	for(int i=1;i<=n;++i){
		b[i][0]=pw(6);
		for(int j=1;j<=3;++j){
			Poly x=Poly();
			x[0]=(j-i+mod)%mod;x[1]=1;
			b[i]=b[i]*x;
		}
	}
	for(int i=1,x;i<=n;++i){
		rd(x),Upd(i,i,x);
		// printf("%d\n",i);
		// for(int j=1;j<=n;++j)Prt(t[j]);
	}
	for(int op,l,r,x;q;--q){
		rd(op),rd(l),rd(r);
		if(l>r)std::swap(l,r);
		if(op&1){
			rd(x),Upd(l,r,x);
		}
		else{
			int ans=(Ask(r)-Ask(l-1)+mod)%mod;
			Add(ans,1ll*(r-l+1)*sum%mod);
			printf("%d\n",ans);
		}
	}
}
posted @ 2026-01-04 22:12  Jordan_Pan  阅读(65)  评论(3)    收藏  举报