BJOI2018链上二次求和——线段树
BJOI2018 链上二次求和
思路
[l,r]的限制可以拆成[1,l-1],[1,r],然后考虑推式子。
设\(s_i\)为\(a_i\)的前缀和。
\({\rm ans}=\sum_{i=1}^{x}\sum_{j=i}^{n}s_{j}-s_{j-i}\)
设\(t_i\)为\(s_i\)的前缀和。
\({\rm ans}=\sum_{i=1}^{x}t_{n}-t_{i-1}-t_{n-i}\)
发现如果我们可以区间维护前缀和的前缀和,那么就可以直接区间查询得到答案了。
考虑一次修改对答案造成的影响,发现对于下标为\(i\)的点的贡献为一个\(ai^{2}+bi+c\)形式的多项式,也就是区间加上一个二次函数。
对于区间加上二次函数,我们可以通过简单的线段树打标记来完成,修改一个点时需要用到二次幂和,直接套用公示即可,标记合并直接相加即可。
/*=======================================
* Author : ylsoi
* Time : 2019.3.31
* Problem : loj2512
* E-mail : ylsoi@foxmail.com
* ====================================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
using namespace std;
void File(){
freopen("loj2512.in","r",stdin);
freopen("loj2512.out","w",stdout);
}
template<typename T>void read(T &_){
_=0; T f=1; char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
_*=f;
}
string proc(){
ifstream f("/proc/self/status");
return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}
const int maxn=2e5+10;
const int mod=1e9+7;
int n,q,s[maxn];
void mul(int &x,int y){
x=1ll*x*y%mod;
}
void inc(int &x,int y){
x+=y;
if(x>=mod)x-=mod;
else if(x<0)x+=mod;
}
int qpow(int x,int y=mod-2){
int ret=1; x%=mod;
while(y){
if(y&1)ret=1ll*ret*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ret;
}
const int inv2=qpow(2);
const int inv6=qpow(6);
#define mid ((l+r)>>1)
#define lc o<<1
#define rc o<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
int sum[maxn<<2],a[maxn<<2],b[maxn<<2],c[maxn<<2];
void build(int o,int l,int r){
if(l==r)sum[o]=s[l];
else{
build(lson),build(rson);
sum[o]=(sum[lc]+sum[rc])%mod;
}
}
int S2(int x){return 1ll*x*(x+1)%mod*(2*x+1)%mod*inv6%mod;}
int S1(int x){return 1ll*x*(x+1)%mod*inv2%mod;}
void pushdown(int o,int l,int r){
if(a[o]){
inc(sum[lc],1ll*(S2(mid)-S2(l-1))*a[o]%mod),inc(a[lc],a[o]);
inc(sum[rc],1ll*(S2(r)-S2(mid))*a[o]%mod),inc(a[rc],a[o]);
}
if(b[o]){
inc(sum[lc],1ll*(S1(mid)-S1(l-1))*b[o]%mod),inc(b[lc],b[o]);
inc(sum[rc],1ll*(S1(r)-S1(mid))*b[o]%mod),inc(b[rc],b[o]);
}
if(c[o]){
inc(sum[lc],1ll*(mid-l+1)*c[o]%mod),inc(c[lc],c[o]);
inc(sum[rc],1ll*(r-mid)*c[o]%mod),inc(c[rc],c[o]);
}
a[o]=b[o]=c[o]=0;
}
void update(int o,int l,int r,int L,int R,int aa,int bb,int cc){
if(L>R)return;
if(L<=l && r<=R){
//debug(l),debug(r)<<endl;
//printf("%d %d %d\n",aa,bb,cc);
//debug(sum[o])<<endl;
inc(sum[o],1ll*(S2(r)-S2(l-1))*aa%mod),inc(a[o],aa);
inc(sum[o],1ll*(S1(r)-S1(l-1))*bb%mod),inc(b[o],bb);
inc(sum[o],1ll*(r-l+1)*cc%mod),inc(c[o],cc);
//debug(sum[o])<<endl;
}
else{
pushdown(o,l,r);
if(L<=mid)update(lson,L,R,aa,bb,cc);
if(R>=mid+1)update(rson,L,R,aa,bb,cc);
sum[o]=(sum[lc]+sum[rc])%mod;
}
}
int query(int o,int l,int r,int L,int R){
if(L>R)return 0;
if(L<=l && r<=R)return sum[o];
int ret=0;
pushdown(o,l,r);
if(L<=mid)inc(ret,query(lson,L,R));
if(R>=mid+1)inc(ret,query(rson,L,R));
return ret;
}
int solve(int x){
return (1ll*query(1,1,n,n,n)*x%mod-query(1,1,n,1,x-1)-query(1,1,n,n-x,n-1))%mod;
}
int main(){
File();
read(n),read(q);
REP(i,1,n)read(s[i]);
REP(i,1,n)inc(s[i],s[i-1]);
REP(i,1,n)inc(s[i],s[i-1]);
build(1,1,n);
int ty,l,r,x;
REP(i,1,q){
read(ty);
if(ty==1){
read(l),read(r),read(x);
if(l>r)swap(l,r);
update(1,1,n,l,r,1ll*inv2*x%mod,1ll*(3-2*l)*inv2%mod*x%mod,1ll*(2-l)*(1-l)%mod*inv2%mod*x%mod);
update(1,1,n,r+1,n,0,1ll*(r-l+1)*x%mod,1ll*(2-l-r)*(r-l+1)%mod*inv2%mod*x%mod);
}
else{
read(l),read(r);
printf("%d\n",((solve(r)-solve(l-1))%mod+mod)%mod);
}
/*REP(j,1,n)printf("%d ",query(1,1,n,j,j));
printf("\n");*/
}
return 0;
}