2019.03.06【BJOI2018】【洛谷P4458】【BZOJ5291】链上二次求和(线段树)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/88257200

洛谷传送门

BZOJ传送门


解析:

直接来看我们要求的式子,设 S S 是点权的前缀和, S S SS S S 的前缀和

一个询问 l , r l,r 表示要求的是
A n s = k = l r i = k n ( S i S i k ) = k = l r ( S S n S S k 1 S S n k ) = ( r l + 1 ) S S n k = l 1 r 1 S S k k = n r n l S S k \begin{aligned} Ans&=&&\sum_{k=l}^r\sum_{i=k}^n(S_i-S_{i-k})\\ &=&&\sum_{k=l}^r(SS_n-SS_{k-1}-SS_{n-k})\\ &=&&(r-l+1)SS_n-\sum_{k=l-1}^{r-1}SS_{k}-\sum_{k=n-r}^{n-l}SS_k \end{aligned}

实际上就是询问 S S SS 的区间和。

我们考虑一个修改 ( l , r , v ) (l,r,v) 会对数组 s s ss 产生什么影响。

我们知道,前缀和的逆运算是差分,所以实际上我们是在 S S SS 的二阶差分数组上面修改,而一个 0 0 次的修改,做两次前缀和,影响是。。。二次函数。

手推一下可以得到,对于 l i r l\leq i\leq r ,影响是: S S i + = ( i l + 1 ) ( i l + 2 ) v 2 SS_i+=\frac{(i-l+1)(i-l+2)v}{2}

而对于后面的数 r < i n r<i\leq n ,由于差分的影响达不到这里,所以这里的变换是一次的,为:

S S i + = l e n ( l e n + 1 ) v 2 + l e n ( i r ) v SS_i+=\frac{len(len+1)v}{2}+len(i-r)v

其中 l e n = r l + 1 len=r-l+1

线段树维护区间加一个与下标有关的二次函数就可以了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int mod=1e9+7,inv2=(mod+1)/2;
inline int add(cs int &a,cs int &b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(cs int &a,cs int &b){return a<b?a-b+mod:a-b;}
inline int mul(cs int &a,cs int &b){return (ll)a*b%mod;}


cs int N=200005;
int n,m;
int a[N<<2],b[N<<2],c[N<<2];
int sum[N<<2];
int d1[N],d2[N];

inline void pushup(cs int &k){sum[k]=add(sum[k<<1],sum[k<<1|1]);}
inline void pushadd(cs int &k,cs int &l,cs int &r,cs int &da,cs int &db,cs int &dc){
	a[k]=add(a[k],da),b[k]=add(b[k],db),c[k]=add(c[k],dc);
	sum[k]=add(sum[k],add(add(mul(da,dec(d2[r],d2[l-1])),mul(db,dec(d1[r],d1[l-1]))),mul(r-l+1,dc)));
}
inline void pushdown(cs int &k,cs int &l,cs int &r){
	if(a[k]||b[k]||c[k]){
		int mid=(l+r)>>1;
		pushadd(k<<1,l,mid,a[k],b[k],c[k]);
		pushadd(k<<1|1,mid+1,r,a[k],b[k],c[k]);
		a[k]=b[k]=c[k]=0;
	}
}

int ss[N];
inline void build(int k,cs int &l,cs int &r){
	if(l==r){
		sum[k]=ss[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}

inline void modify(int k,cs int &l,cs int &r,cs int &ql,cs int &qr,cs int &a,cs int &b,cs int &c){
	if(ql<=l&&r<=qr)return pushadd(k,l,r,a,b,c);
	int mid=(l+r)>>1;
	pushdown(k,l,r);
	if(ql<=mid)modify(k<<1,l,mid,ql,qr,a,b,c);
	if(mid<qr)modify(k<<1|1,mid+1,r,ql,qr,a,b,c);
	pushup(k);
}

inline int query(int k,cs int &l,cs int &r,cs int &ql,cs int &qr){
	if(ql<=l&&r<=qr)return sum[k];
	pushdown(k,l,r);
	int mid=(l+r)>>1;
	if(qr<=mid)return query(k<<1,l,mid,ql,qr);
	if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
	return add(query(k<<1,l,mid,ql,qr),query(k<<1|1,mid+1,r,ql,qr));
}

signed main(){
	n=getint(),m=getint();
	for(int re i=1;i<=n;++i)ss[i]=add(getint(),ss[i-1]);
	for(int re i=1;i<=n;++i)ss[i]=add(ss[i],ss[i-1]);
	for(int re i=1;i<=n;++i)d1[i]=add(d1[i-1],i),d2[i]=add(d2[i-1],mul(i,i));
	build(1,0,n);
	int l,r,v,a,b,c,len;
	while(m--){
		switch(getint()){
			case 1:{
				l=getint(),r=getint();
				if(l>r)swap(l,r);
				v=getint();
				a=inv2;
				b=mul(dec(3,add(l,l)),inv2);
				c=mul(mul(l-1,l-2),inv2);
				modify(1,0,n,l,r,mul(a,v),mul(b,v),mul(c,v));
				if(r==n)break;
				len=r-l+1;
				modify(1,0,n,r+1,n,0,mul(r-l+1,v),mul(v,dec(mul(inv2,mul(len,len+1)),mul(len,r))));
				break;
			}
			case 2:{
				l=getint(),r=getint();
				cout<<dec(mul(query(1,0,n,n,n),r-l+1),add(query(1,0,n,l-1,r-1),query(1,0,n,n-r,n-l)))<<"\n";
				break;
			}
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/88257200
今日推荐