【BJOI2018】【BZOJ2591】—链上二次求和(线段树维护二次函数)

传送门

开始还是挺好想的,就是求前缀和的前缀和
S S S表示前缀和,则
a n s = ∑ k = l r ∑ i = k n ( S k − S i − k ) ans=\sum_{k=l}^{r}\sum_{i=k}^n(S_k-S_{i-k}) ans=k=lri=kn(SkSik)

= ∑ k = l r ( ∑ i = k n S i − ∑ i = 0 n − k S i ) =\sum_{k=l}^{r}(\sum_{i=k}^{n}S_i-\sum_{i=0}^{n-k}S_i) =k=lr(i=knSii=0nkSi)

S S SS SS表示 S S S的前缀和

a n s = ∑ k = l r ( S S n − S S k − 1 − S S n − k ) ans=\sum_{k=l}^{r}(SS_n-SS_{k-1}-SS_{n-k}) ans=k=lr(SSnSSk1SSnk)
= ( r − l + 1 ) S S n − ∑ i = l − 1 r − 1 S S i − ∑ i = n − l n − r S S i =(r-l+1)SS_n-\sum_{i=l-1}^{r-1}SS_i-\sum_{i=n-l}^{n-r}SS_i =(rl+1)SSni=l1r1SSii=nlnrSSi

发现我们只需要维护一个 S S SS SS的区间和

再考虑一次修改对答案的影响

i ∈ [ l , r ] i\in[l,r] i[l,r]
S S i + = ( i − l + 1 ) ∗ ( i − l + 2 ) 2 ∗ k SS_i+=\frac{(i-l+1)*(i-l+2)}{2}*k SSi+=2(il+1)(il+2)k

i ∈ [ r + 1 , n ] i\in [r+1,n] i[r+1,n]
S S i + = ( l e n ∗ ( l e n + 1 ) 2 + l e n ∗ ( i − r ) ) ∗ k SS_i+=(\frac{len*(len+1)}{2}+len*(i-r))*k SSi+=(2len(len+1)+len(ir))k

拆开后发现是一个二次函数 a i 2 + b i + c ai^2+bi+c ai2+bi+c的形式,可以维护一下一次修改的 a , b , c a,b,c a,b,c

用线段树就可以了
注意有除法,可以先不除2,把所有乘2,最后再乘一个逆元就可以了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
inline int read(){
    char ch=getchar();
    int res=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=getchar();
    return res*f;
}	
const int N=200005;
const ll mod=1e9+7;
const ll inv2=5e8+4;
int n,m;
ll ss[N],f1[N],f2[N],a[N<<2],b[N<<2],c[N<<2],tr[N<<2];
#define lc (u<<1)
#define rc ((u<<1)|1)
#define mid ((l+r)>>1)
inline void pushup(int u){
	tr[u]=(tr[lc]+tr[rc])%mod;
}
inline void pushnow(int u,int l,int r,ll a1,ll b1,ll c1){
	(a[u]+=a1)%=mod,(b[u]+=b1)%=mod,(c[u]+=c1)%=mod;
	(tr[u]+=(a1*(f2[r]-f2[l-1])%mod)+b1*(f1[r]-f1[l-1])%mod+c1*(r-l+1)%mod)%=mod;
}
inline void pushdown(int u,int l,int r){
	if(!a[u]&&!b[u]&&!c[u])return;
	pushnow(lc,l,mid,a[u],b[u],c[u]);
	pushnow(rc,mid+1,r,a[u],b[u],c[u]);
	a[u]=b[u]=c[u]=0;
}
void build(int u,int l,int r){
	if(l==r){tr[u]=ss[l];return;}
	build(lc,l,mid),build(rc,mid+1,r);
	pushup(u);
}
void update(int u,int l,int r,int st,int des,ll a1,ll b1,ll c1){
	if(st<=l&&r<=des){pushnow(u,l,r,a1,b1,c1);return;}
	pushdown(u,l,r);
	if(st<=mid)update(lc,l,mid,st,des,a1,b1,c1);
	if(mid<des)update(rc,mid+1,r,st,des,a1,b1,c1);
	pushup(u);
}
ll query(int u,int l,int r,int st,int des){
	if(st<=l&&r<=des)return tr[u];
	ll res=0;pushdown(u,l,r);
	if(st<=mid)res+=query(lc,l,mid,st,des);
	if(mid<des)res+=query(rc,mid+1,r,st,des);
	pushup(u);
	return res%mod;
}
inline void modify(int l,int r,ll k){
	if(l>r)swap(l,r);
	ll a1=k,b1=(3-2*l+mod)%mod*k%mod,c1=((l*l%mod-3*l+2)%mod+mod)%mod*k%mod;
	update(1,0,n,l,r,a1,b1,c1);
	if(r==n)return;//
	ll len=r-l+1;
	a1=0,b1=len*k*2%mod,c1=(len*(len+1-2*r)%mod+mod)%mod*k%mod;
	update(1,0,n,r+1,n,a1,b1,c1);
}
inline ll ask(int l,int r){
	ll res=((1ll*(r-l+1)*query(1,0,n,n,n)%mod-query(1,0,n,l-1,r-1)-query(1,0,n,n-r,n-l))%mod+mod)%mod;
	return res*inv2%mod;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)(ss[i]=ss[i-1]+read())%=mod;
	for(int i=1;i<=n;i++)(ss[i]+=ss[i-1])%mod;
	for(int i=1;i<=n;i++)(ss[i]*=2)%=mod,f1[i]=(f1[i-1]+i)%mod,f2[i]=(f2[i-1]+1ll*i*i%mod)%mod;
	build(1,0,n);
	while(m--){
		int op=read();
		int l=read(),r=read();
		if(op==1)modify(l,r,read());
		else cout<<ask(l,r)<<'\n';
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值