单点修改,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)\)。
代码
#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);
}
}
}

浙公网安备 33010602011771号