【BZOJ】5291: [Bjoi2018]链上二次求和 -线段树&二次前缀和

传送门:bzoj5291


题解

S S S a a a的前缀和, S S SS SS S S S的前缀和。

每次询问所求 ∑ k = l r ∑ i = k n ( S i − S i − k ) \sum\limits_{k=l}^r\sum\limits_{i=k}^{n}(S_i-S_{i-k}) k=lri=kn(SiSik)可化简为:
( r − l + 1 ) S S n − ∑ i = l − 1 r − 1 S S i − ∑ i = n − r n − l S S i (r-l+1)SS_n-\sum\limits_{i=l-1}^{r-1}SS_i-\sum\limits_{i=n-r}^{n-l}SS_i (rl+1)SSni=l1r1SSii=nrnlSSi

这是一个关于 i i i的二元一次函数,可以化成 a i 2 + b i + c ai^2+bi+c ai2+bi+c的形式用线段树维护标记下传。


代码

#include
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=2e5+10,mod=1e9+7;
const int iv2=5e8+4;
typedef long long ll;

int n,m,a[N],pa[N],pb[N];

char cp,OS[100];
inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

inline void ot(int x)
{
	int re=0;OS[0]='\n';
	for(;(!re)||(x);x/=10) OS[++re]='0'+x%10;
	for(;~re;--re) putchar(OS[re]);
}

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}

struct node{
   int s,a,b,c;
   inline void chg(node x,int l,int r)
   {
   	   if(x.a) a=ad(a,x.a),s=ad(s,(ll)x.a*dc(pa[r],pa[l-1])%mod);
   	   if(x.b) b=ad(b,x.b),s=ad(s,(ll)x.b*dc(pb[r],pb[l-1])%mod);
   	   if(x.c) c=ad(c,x.c),s=ad(s,(ll)x.c*(r-l+1)%mod);
   }
   inline void mul(int z){a=(ll)a*z%mod;b=(ll)b*z%mod;c=(ll)c*z%mod;}
}t[N<<2],tp;

void build(int k,int l,int r)
{
	if(l==r) {t[k].s=a[l];return;}
	build(lc,l,mid);build(rc,mid+1,r);
	t[k].s=ad(t[lc].s,t[rc].s);
}

inline void pushdown(int k,int l,int r)
{
	if((!t[k].a)&&(!t[k].b)&&(!t[k].c)) return;
	t[lc].chg(t[k],l,mid);t[rc].chg(t[k],mid+1,r);
	t[k].a=t[k].b=t[k].c=0;
}

void ad(int k,int l,int r,int L,int R)
{
	if(L<=l && r<=R){t[k].chg(tp,l,r);return;}
	pushdown(k,l,r);
	if(L<=mid) ad(lc,l,mid,L,R);
	if(R>mid) ad(rc,mid+1,r,L,R);
	t[k].s=ad(t[lc].s,t[rc].s);
}

int ask(int k,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return t[k].s;
	pushdown(k,l,r);
	if(R<=mid) return ask(lc,l,mid,L,R);
	if(L>mid) return ask(rc,mid+1,r,L,R);
	return ad(ask(lc,l,mid,L,R),ask(rc,mid+1,r,L,R));
}

int main(){
	int i,op,x,y,z,re;
	rd(n);rd(m);
	for(i=1;i<=n;++i) {rd(a[i]);a[i]=ad(a[i],a[i-1]);}
	for(i=2;i<=n;++i) a[i]=ad(a[i],a[i-1]);
	for(i=1;i<=n;++i) {pb[i]=ad(pb[i-1],i);pa[i]=ad(pa[i-1],(ll)i*i%mod);}
	build(1,1,n);
	for(;m;--m){
		rd(op);rd(x);rd(y);if(x>y) swap(x,y);
		if(op==1){
		   tp=(node){0,iv2,(ll)dc(3,ad(x,x))*iv2%mod,(ll)dc(ad((ll)x*x%mod,2),(ll)3*x%mod)*iv2%mod};
		   rd(z);tp.mul(z);ad(1,1,n,x,y);
		   if(y1) z=ask(1,1,n,max(1,x-1),y-1);
		   if(n>x) z=ad(z,ask(1,1,n,max(1,n-y),n-x));
		   ot(dc(re,z));
		}
	}
	return 0;
}

你可能感兴趣的:(线段树)