[BJOI2018] 链上二次求和

一、题目

点此看题

二、解法

把答案转化成前缀和的形式,设 \(s_i\) 表示原数组的一阶前缀和,那么答案是:

\[\sum_{i=l}^{r}\sum_{j=i}^n s_j-s_{j-i} \]

\(ss_i\) 表示原数组的二阶前缀和(也就是对前缀和再做一次前缀和),答案可以写成:

\[\sum_{i=l}^{r} ss_n-ss_{n-i}-ss_{i-1} \]

所以我们只需要拿一棵线段树维护原数组的二阶前缀和即可。考虑区间修改 \([l,r]\) 对于二阶前缀和的影响,通过贡献法可以计算出变化为(设 \(G(n)=\frac{n(n+1)}{2}\)):

  • 如果 \(i\in[l,r]\)\(ss_i\) 会增加 \(G(i-l+1)\cdot d\)
  • 如果 \(i\in(r,n]\)\(ss_i\) 会增加 \([G(r-l+1)+(i-r)\cdot (r-l+1)]\cdot d\)

\(ss_i=a_i\cdot i^2+b_i\cdot i+c_i\),在线段树上维护这三项即可,所有修改操作都可以转化成对于这三项的修改。

比如第一种操作,\(G(i-l+1)=\frac{(i-l+1)(i-l+2)}{2}\),那么 \(a_i=\frac{1}{2},b_i=\frac{3-2l}{2},c_i=\frac{l^2-3l+2}{2}\)

时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 800005;
#define int long long
const int MOD = 1e9+7;
const int inv2 = (MOD+1)/2;
const int inv6 = 166666668;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,s[M],a[M],b[M],c[M],t[M];
void add(int &x,int y) {x=(x+y)%MOD;}
int s1(int n) {return n*(n+1)/2%MOD;}
int s2(int n) {return n*(n+1)%MOD*(2*n+1)%MOD*inv6%MOD;}
void get(int i,int l,int r,int da,int db,int dc)
{
	add(a[i],da);add(b[i],db);add(c[i],dc);
	add(t[i],da*(s2(r)-s2(l-1))+db*(s1(r)-s1(l-1))+dc*(r-l+1));
}
void down(int i,int l,int r)
{
	if(!a[i] && !b[i] && !c[i]) return ;
	int mid=(l+r)>>1;
	get(i<<1,l,mid,a[i],b[i],c[i]);
	get(i<<1|1,mid+1,r,a[i],b[i],c[i]);
	a[i]=b[i]=c[i]=0;
}
void build(int i,int l,int r)
{
	if(l==r) {t[i]=s[l];return ;}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	t[i]=(t[i<<1]+t[i<<1|1])%MOD;
}
void upd(int i,int l,int r,int L,int R,int a,int b,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) {get(i,l,r,a,b,c);return ;}
	int mid=(l+r)>>1;down(i,l,r);
	upd(i<<1,l,mid,L,R,a,b,c);
	upd(i<<1|1,mid+1,r,L,R,a,b,c);
	t[i]=(t[i<<1]+t[i<<1|1])%MOD;
}
int ask(int i,int l,int r,int L,int R)
{
	if(L>r || l>R) return 0;
	if(L<=l && r<=R) return t[i];
	int mid=(l+r)>>1;down(i,l,r);
	return ask(i<<1,l,mid,L,R)+ask(i<<1|1,mid+1,r,L,R);
}
void Upd(int l,int r,int d)
{
	int len=r-l+1;
	upd(1,0,n,l,r,d*inv2%MOD,(3-2*l)*d%MOD*inv2%MOD,
	(l*l-3*l+2)%MOD*d%MOD*inv2%MOD);
	if(r<n) upd(1,0,n,r+1,n,0,len*d%MOD,(s1(len)-len*r)%MOD*d%MOD);
}
int Ask(int l,int r)
{
	return ((ask(1,0,n,n,n)%MOD*(r-l+1)-ask(1,0,n,l-1,r-1)
	-ask(1,0,n,n-r,n-l))%MOD+MOD)%MOD;
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
		s[i]=(s[i-1]+read())%MOD;
	for(int i=1;i<=n;i++)
		s[i]=(s[i-1]+s[i])%MOD;
	build(1,0,n);
	while(m--)
	{
		int op=read(),l=read(),r=read();
		if(l>r) swap(l,r);
		if(op==1) Upd(l,r,read());
		else printf("%lld\n",Ask(l,r));
	}
}
posted @ 2022-07-25 15:27  C202044zxy  阅读(164)  评论(0编辑  收藏  举报