洛谷传送门
BZOJ传送门
解析:
直接来看我们要求的式子,设 S S S是点权的前缀和, S S SS SS是 S S S的前缀和
一个询问
l
,
r
l,r
l,r表示要求的是
A
n
s
=
∑
k
=
l
r
∑
i
=
k
n
(
S
i
−
S
i
−
k
)
=
∑
k
=
l
r
(
S
S
n
−
S
S
k
−
1
−
S
S
n
−
k
)
=
(
r
−
l
+
1
)
S
S
n
−
∑
k
=
l
−
1
r
−
1
S
S
k
−
∑
k
=
n
−
r
n
−
l
S
S
k
\begin{aligned} Ans&=&&\sum_{k=l}^r\sum_{i=k}^n(S_i-S_{i-k})\\ &=&&\sum_{k=l}^r(SS_n-SS_{k-1}-SS_{n-k})\\ &=&&(r-l+1)SS_n-\sum_{k=l-1}^{r-1}SS_{k}-\sum_{k=n-r}^{n-l}SS_k \end{aligned}
Ans===k=l∑ri=k∑n(Si−Si−k)k=l∑r(SSn−SSk−1−SSn−k)(r−l+1)SSn−k=l−1∑r−1SSk−k=n−r∑n−lSSk
实际上就是询问 S S SS SS的区间和。
我们考虑一个修改 ( l , r , v ) (l,r,v) (l,r,v)会对数组 s s ss ss产生什么影响。
我们知道,前缀和的逆运算是差分,所以实际上我们是在 S S SS SS的二阶差分数组上面修改,而一个 0 0 0次的修改,做两次前缀和,影响是。。。二次函数。
手推一下可以得到,对于 l ≤ i ≤ r l\leq i\leq r l≤i≤r,影响是: S S i + = ( i − l + 1 ) ( i − l + 2 ) v 2 SS_i+=\frac{(i-l+1)(i-l+2)v}{2} SSi+=2(i−l+1)(i−l+2)v
而对于后面的数 r < i ≤ n r<i\leq n r<i≤n,由于差分的影响达不到这里,所以这里的变换是一次的,为:
S S i + = l e n ( l e n + 1 ) v 2 + l e n ( i − r ) v SS_i+=\frac{len(len+1)v}{2}+len(i-r)v SSi+=2len(len+1)v+len(i−r)v
其中 l e n = r − l + 1 len=r-l+1 len=r−l+1。
线段树维护区间加一个与下标有关的二次函数就可以了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
cs int mod=1e9+7,inv2=(mod+1)/2;
inline int add(cs int &a,cs int &b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(cs int &a,cs int &b){return a<b?a-b+mod:a-b;}
inline int mul(cs int &a,cs int &b){return (ll)a*b%mod;}
cs int N=200005;
int n,m;
int a[N<<2],b[N<<2],c[N<<2];
int sum[N<<2];
int d1[N],d2[N];
inline void pushup(cs int &k){sum[k]=add(sum[k<<1],sum[k<<1|1]);}
inline void pushadd(cs int &k,cs int &l,cs int &r,cs int &da,cs int &db,cs int &dc){
a[k]=add(a[k],da),b[k]=add(b[k],db),c[k]=add(c[k],dc);
sum[k]=add(sum[k],add(add(mul(da,dec(d2[r],d2[l-1])),mul(db,dec(d1[r],d1[l-1]))),mul(r-l+1,dc)));
}
inline void pushdown(cs int &k,cs int &l,cs int &r){
if(a[k]||b[k]||c[k]){
int mid=(l+r)>>1;
pushadd(k<<1,l,mid,a[k],b[k],c[k]);
pushadd(k<<1|1,mid+1,r,a[k],b[k],c[k]);
a[k]=b[k]=c[k]=0;
}
}
int ss[N];
inline void build(int k,cs int &l,cs int &r){
if(l==r){
sum[k]=ss[l];
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
inline void modify(int k,cs int &l,cs int &r,cs int &ql,cs int &qr,cs int &a,cs int &b,cs int &c){
if(ql<=l&&r<=qr)return pushadd(k,l,r,a,b,c);
int mid=(l+r)>>1;
pushdown(k,l,r);
if(ql<=mid)modify(k<<1,l,mid,ql,qr,a,b,c);
if(mid<qr)modify(k<<1|1,mid+1,r,ql,qr,a,b,c);
pushup(k);
}
inline int query(int k,cs int &l,cs int &r,cs int &ql,cs int &qr){
if(ql<=l&&r<=qr)return sum[k];
pushdown(k,l,r);
int mid=(l+r)>>1;
if(qr<=mid)return query(k<<1,l,mid,ql,qr);
if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
return add(query(k<<1,l,mid,ql,qr),query(k<<1|1,mid+1,r,ql,qr));
}
signed main(){
n=getint(),m=getint();
for(int re i=1;i<=n;++i)ss[i]=add(getint(),ss[i-1]);
for(int re i=1;i<=n;++i)ss[i]=add(ss[i],ss[i-1]);
for(int re i=1;i<=n;++i)d1[i]=add(d1[i-1],i),d2[i]=add(d2[i-1],mul(i,i));
build(1,0,n);
int l,r,v,a,b,c,len;
while(m--){
switch(getint()){
case 1:{
l=getint(),r=getint();
if(l>r)swap(l,r);
v=getint();
a=inv2;
b=mul(dec(3,add(l,l)),inv2);
c=mul(mul(l-1,l-2),inv2);
modify(1,0,n,l,r,mul(a,v),mul(b,v),mul(c,v));
if(r==n)break;
len=r-l+1;
modify(1,0,n,r+1,n,0,mul(r-l+1,v),mul(v,dec(mul(inv2,mul(len,len+1)),mul(len,r))));
break;
}
case 2:{
l=getint(),r=getint();
cout<<dec(mul(query(1,0,n,n,n),r-l+1),add(query(1,0,n,l-1,r-1),query(1,0,n,n-r,n-l)))<<"\n";
break;
}
}
}
return 0;
}