P4458 [BJOI2018] 链上二次求和

补了,参考第二篇题解,感觉没那么难写吧,之后倒是可以整一个整数取模类拿这个题测。

由标签,考虑推式子,令 \(s_i = \sum_{j=1}^i a_j\)\(ss_i = \sum_{i=1}^j s_j\),即一二次前缀和。

要求 \(\sum_{k=l}^r \sum_{L=1}^{n-k+1} \sum_{i=L}^{L+k-1} a_i\),这个形式不好看,转一下 \(\sum_{k=l}^r \sum_{R=k}^{n} \sum_{i=R-k+1}^{R} a_i\),之后有:

\[\sum_{k=l}^r \sum_{R=k}^{n} \sum_{i=R-k+1}^{R} a_i = \sum_{k=l}^r \sum_{R=k}^n (s_R - s_{R-k}) = \sum_{k=l}^r (ss_n - ss_{k-1} - ss_{n-k}) = (r-l+1)ss_n - \sum_{i=l-1}^{r-1} ss_i - \sum_{i=n-r}^{n-l} ss_i \]

所以我们只要维护 \(ss_i\) 的区间查询就好了。

对于区间修改,我们令这一次操作的增值为 \(k\),那么有:

对于 \(l \leq i \leq r\),增值为 \(\frac{(r-l+1)(r-l+2)k}{2}\),对于 \(r < i \leq n\),增值为 \(\frac{(r-l+1)(r-l+2)k}{2} + (i-r)(r-l+1)k\)

之后可以将这两个式子手动展开,对每一个线段树节点维护一个二次的 lazy 式子就好了,具体来讲,维护 a,b,c,表示这一个线段树节点需要增值 \(\sum ai^2+bi+c\)

由于已知 \(\sum i^2 = \frac{a(a+1)(2a+1)}{6}\)\(\sum i = \frac{r-l+1}{2}\),这个东西就很好维护了。

建议写一个取模类,或者把取模非负的操作写成函数,一个一个取模太容易错了,查询时对于左端点 \(\leq 0\) 的情况也是有贡献的,记得特判。

#include<bits/stdc++.h>
#define pr putchar('\n')
#define fo(a,i,b) for(register ll i = a ; i <= b ; ++ i )
#define Fo(a,i,b) for(register ll i = a ; i >= b ; -- i )
using namespace std;
typedef long long ll;
inline void read(ll &opp){ll x=0,t=1;char ch;ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}opp=x*t;return; }
inline void wr(ll x){if(x<0){putchar('-');x=-x;}if(x>9){wr(x/10);}putchar(x%10+'0');}
const ll N=2e5+5,mod=1e9+7,inv2=500000004,inv6=166666668;
ll n,m,a[N],s[N],ss[N];
inline ll ad(ll a,ll b){return ((a+b)%mod+mod)%mod;}
inline ll add(ll a,ll b,ll c=0ll,ll d=0ll){return ad(ad(a,b),ad(c,d));}
inline ll mu(ll a,ll b){return (a*b%mod+mod)%mod;}
inline ll mul(ll a,ll b,ll c=1ll,ll d=1ll){return mu(mu(a,b),mu(c,d));}
inline ll sum1(ll a){return mul(a,a+1ll,inv2);}
inline ll gsum1(ll a,ll b){return ad(sum1(b),-sum1(a-1ll));}
inline ll sum2(ll a){return mul(a,a+1ll,2ll*a+1ll,inv6);}
inline ll gsum2(ll a,ll b){return ad(sum2(b),-sum2(a-1ll));}
struct SGT{ll l,r,sum,la,lb,lc;}tree[N<<2];
#define rt tree[root]
#define ls tree[root<<1]
#define rs tree[root<<1|1]
inline void pushup(ll root){rt.sum=ad(ls.sum,rs.sum);}
inline void pdtag(ll root,ll la,ll lb,ll lc){ll l=rt.l,r=rt.r;rt.sum=add(rt.sum,mul(la,gsum2(l,r)),mul(lb,gsum1(l,r)),mul(lc,(r-l+1))),rt.la=ad(rt.la,la),rt.lb=ad(rt.lb,lb),rt.lc=ad(rt.lc,lc);}
inline void pd(ll root){if(rt.la||rt.lb||rt.lc) pdtag(root<<1,rt.la,rt.lb,rt.lc),pdtag(root<<1|1,rt.la,rt.lb,rt.lc),rt.la=rt.lb=rt.lc=0;}
inline void build(ll root,ll l,ll r){rt.l=l,rt.r=r;if(l==r) return rt.sum=ss[l],void();ll mid=l+r>>1;build(root<<1,l,mid),build(root<<1|1,mid+1,r);pushup(root);}
inline void upd(ll root,ll x,ll y,ll la,ll lb,ll lc){x=max(1ll,x);if(x>y||x<=0||y<=0) return;ll l=rt.l,r=rt.r;;if(x<=l&&y>=r) return pdtag(root,la,lb,lc),void();ll mid=l+r>>1;pd(root);if(x<=mid) upd(root<<1,x,y,la,lb,lc);if(y>mid) upd(root<<1|1,x,y,la,lb,lc);pushup(root);}
inline ll ask(ll root,ll x,ll y){x=max(1ll,x);if(x>y||x<=0||y<=0||x>n||y>n) return 0;ll l=rt.l,r=rt.r;if(x<=l&&y>=r) return rt.sum;ll mid=l+r>>1,ans=0;pd(root);if(x<=mid) ans=ask(root<<1,x,y);if(y>mid) ans=ad(ans,ask(root<<1|1,x,y));return ans;}
inline void upd(ll l,ll r,ll d){upd(1,l,r,mu(inv2,d),mu(ad(3ll,mu(-2ll,l)),mu(inv2,d)),mul(ad(l,-1ll),ad(l,-2ll),mu(inv2,d))),upd(1,r+1,n,0,mu(r-l+1,d),add(mul(r-l+1,r-l+2,inv2,d),mul(-r,r-l+1,d))); }
inline ll ask(ll l,ll r){return add(mu(r-l+1,ask(1,n,n)),-ask(1,l-1,r-1),-ask(1,n-r,n-l));}
signed main(){
	read(n),read(m);fo(1,i,n) read(a[i]),s[i]=ad(a[i],s[i-1]),ss[i]=ad(s[i],ss[i-1]);build(1,1,n);
	fo(1,i,m){ll opt,u,v,k;read(opt),read(u),read(v);(opt==1?(read(k),upd(min(u,v),max(u,v),k)):(wr(ask(u,v)),pr,void()));}
	return 0;
}
posted @ 2025-03-16 14:39  Wei_Han  阅读(10)  评论(0)    收藏  举报