前言
优秀的高阶差分/前缀和练习题。
Solution
思路
题意很清晰,不加以赘述。
注意到区间修改,但是我们发现修改的不同,因此设法将其转化为相同的值。
经过打表或手膜等一系列方式,我们发现修改值之间存在一些美妙的关系。
考虑差分。
- $k=1$ 时
一次差分后的差分序列的值均为 1 ,可以配合数据结构修改。
但更好的方法是二次差分,转化为形如 ${1,0,0,0… ,-1}$ 单点修改的形式。
- $k=2$ 时
经过一次差分我们发现其转化为了上述 $k=1$ 的形式。
直接三次差分。
- $k = x$ 时
我们从 $k$ 较小的情况推广,容易发现我们修改的应该是 $k+1$ 阶差分数组。
这也不难通过组合意义证明,另一篇题解已经讲得很清楚了。
具体实现
我们发现在具体实现中,难以直接对 $k$ 阶差分数组进行修改。
因为每次差分一次后,修改的序列都会增长。
具体例子如下:
修改序列 1 2 3 4 5 0 0 0
一阶差分 1 1 1 1 1 -5 0 0
二阶差分 1 0 0 0 0 -6 5 0
我们不难发现,每次为了消除上一阶差分的影响,都需要多伸长一位。
对此,我们可以对于每一阶差分直接消除其影响。
再次以上面的为例:
修改序列 1 2 3 4 5 0 0 0
一阶差分 0 0 0 0 0 -5 0 0
二阶差分 1 0 0 0 0 -1 0 0
每一阶在 $r+1$ 位置都删去这一阶的前缀,也就是上一阶的最后一个数,即为 $\dbinom{r-l+k-j+1}{r-l}$ 。
这样最终只需要从下向上做前缀和即可。
Code
//#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define N 200005
#define int long long
using namespace std;
int read(){
int x=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x*w;
}
int mod = 1e9+7;
int n,m;
int fac[N],inv[N];
int a[N],d[100005][105];
int qpow(int x,int k){
int res=1;
while(k){
if(k&1) res=res*x%mod;
x=x*x%mod;k>>=1;
}
return res;
}
void init(){
fac[0]=inv[0]=1; int maxn = 200000;
for(int i=1;i<=maxn;++i) fac[i]=fac[i-1]*i%mod;
inv[maxn]=qpow(fac[maxn],mod-2);
for(int i=maxn-1;i>=1;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int x,int y){return fac[x]*inv[y]%mod*inv[x-y]%mod;}
signed main(){
init(); //cout<<C(4,2);
n=read();m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i){
int l=read(),r=read(),k=read();
d[l][k+1]=(d[l][k+1]+1)%mod;
for(int j=1;j<=k+1;++j) d[r+1][j]=(d[r+1][j]-C(r-l+k-j+1,r-l)+mod)%mod;
}
for(int i=101;i>=1;--i){
int res=0;
for(int j=1;j<=n;++j) {
res=(res+d[j][i])%mod;
d[j][i-1]=(d[j][i-1]+res)%mod;
}
}
for(int i=1;i<=n;++i) printf("%lld ",(a[i]+d[i][0])%mod);
return 0;
}