链上二次求和

link

正在肝的一道黑题……

首先处理那个奇怪的询问操作。我们可以对于那个奇怪的东西进行暴力推式:

\[ans(l,r)=\sum\limits_{len=l}^r\sum\limits_{i=1}^{m-len+1}\sum\limits_{j=i}^{i+len-1}{a_j} \]

用前缀和搞一下就是

\[\sum\limits_{len=l}^r\sum\limits_{i=1}^{m-len+1}{sum_{i+len-1}-sum_{i-1}} \]

分离一下

\[\sum\limits_{len=l}^r{(\sum\limits_{i=len}^m{sum_i}-\sum\limits_{i=0}^{m-len}sum_i)} \]

emmmm,发现它似乎仍然具有前缀和的性质,考虑搞个前缀和的前缀和(\(su\)):

\[\sum\limits_{len=l}^r{su_{m}-su_{len-1}-su_{m-len}} \]

展开:

\[su_m\times(r-l+1)-\sum\limits_{i=l-1}^{r-1}su_i-\sum\limits_{i=m-r}^{m-l}{su_i} \]

于是那个奇怪的询问就变成了单纯的对于su数组的区间和查询。

按照思考此类问题的惯常方法,考虑假如我们给\([l,r]\)中的数都加了一个\(a\),会发生些什么?

\[\Delta sum_i=\begin{cases}0&i\in[1,l-1]\\d\times(i-l+1)&i\in[l,r]\\d\times(r-l+1)&i\in[r+1,m]\end{cases} \]

同样可以得出,假如\(s(i)=i\times(i+1)/2\)

\[\Delta su_i=\begin{cases}0&i\in[1,l-1]\\d\times s(i-l+1)&i\in[l,r]\\d\times s(r-l+1)+d\times(r-l+1)\times(i-r)&i\in[r+1,m]\end{cases} \]

开心地发现\(i\in[1,l-1]\)部分不用管,所以对于剩下的两个部分进行处理。首先是\(i\in[r+1,M]\)部分,令\(len=r-l+1\)

\[\Delta su_i=d\times s(len)+d\times len\times(i-r)\\=d\times s(len)-d\times len\times r+d\times len\times r \]

发现前半部分是个定值,相当于是普通区间加;后半部分相当于是区间加 \(d\times len\) 倍的下标。如何实现后面再说。再看一下对于 \(i\in[r+1,m]\) 部分怎么处理。

\[\frac{2\Delta su_i}{d}=(i-l+1)^2+(i-l+1)\\=i^2+(l-1)^2+2i(1-l)+i+(1-l)\\=i^2+(3-2l)i+(l-1)(l-2) \]

所以

\[2\Delta su_i=di^2+(3d-2ld)i+(l-1)(l-2)d \]

发现\((l-1)(l-2)\)是固定的,相当于是区间加。于是我们就需要一种数据结构可以完成区间加下标平方和区间加下标。这应该是可以做到的。区间加下标好办,那区间加下标平方曾么搞?有平方和公式啊。

\[\sum\limits_{i=1}^{m}{i^2}=\frac{m(m+1)(2m+1)}{6} \]

最后我们发现,上面推出来的柿子为真正增量的两倍。取模意义下除法灰常麻烦,于是我的做法是线段树中维护的是增量的两倍,询问时乘上2的逆元就可以了。

最后说明一点,由于这道题对取模的要求苛刻得有些丧心病狂,一定要认真取模。然后本蒟蒻代码能力极差,码风自然氢气,见谅。

#include<cstdio>
//#define zczc
#define int long long
const int N=200010;
const int yy=500000004;
const int mod=1000000007;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

int m,n,a[N];

#define lc (wh<<1)
#define rc (wh<<1|1)
#define mid (t[wh].l+t[wh].r>>1)
//直接区间加的那棵树
namespace ta{
	struct node{
		int l,r,data,lazy;
	}t[N<<2];
	inline void pushup(int wh){
		t[wh].data=(t[lc].data+t[rc].data)%mod;
		return;
	}
	inline void pushnow(int wh,int val){
		t[wh].lazy+=val;
		t[wh].lazy%=mod;
		t[wh].data+=val*(t[wh].r-t[wh].l+1)%mod;
		t[wh].data%=mod;
		return;
	}
	inline void pushdown(int wh){
		if(t[wh].lazy){
			pushnow(lc,t[wh].lazy);
			pushnow(rc,t[wh].lazy);
			t[wh].lazy=0;
		}
		return;
	}
	inline void build(int wh,int l,int r){
		t[wh].l=l,t[wh].r=r;
		if(l==r){
			t[wh].data=a[l];
			return;
		}
		build(lc,l,mid);
		build(rc,mid+1,r);
		pushup(wh);
		return;
	}
	inline void change(int wh,int wl,int wr,int val){
		if(wl<=t[wh].l&&t[wh].r<=wr){
			pushnow(wh,val);
			return;
		}
		pushdown(wh);
		if(wl<=mid)change(lc,wl,wr,val);
		if(wr>mid)change(rc,wl,wr,val);
		pushup(wh);
		return; 
	}
	inline int work(int wh,int wl,int wr){
		if(wl<=t[wh].l&&t[wh].r<=wr){
			return t[wh].data;
		}
		pushdown(wh);
		int an=0;
		if(wl<=mid)an+=work(lc,wl,wr);
		if(wr>mid)an+=work(rc,wl,wr);
		return an%mod; 
	}
	inline int ask(int wh,int pl){
		if(t[wh].l==t[wh].r)return t[wh].data;
		pushdown(wh);
		if(pl<=mid)return ask(lc,pl);
		else return ask(rc,pl);
	}
}
//区间加下标的那棵树 
namespace tb{
	struct node{
		int l,r,data,lazy;
	}t[N<<2];
	inline void pushup(int wh){
		t[wh].data=(t[lc].data+t[rc].data)%mod;
		return;
	}
	inline int s(int l,int r){
		return (l+r)*(r-l+1)/2%mod;
	}
	inline void pushnow(int wh,int val){
		t[wh].lazy+=val;t[wh].lazy%=mod;
		t[wh].data+=val*s(t[wh].l,t[wh].r)%mod;
		t[wh].data%=mod;
		return;
	}
	inline void pushdown(int wh){
		if(t[wh].lazy){
			pushnow(lc,t[wh].lazy);
			pushnow(rc,t[wh].lazy);
			t[wh].lazy=0;
		}
		return;
	}
	inline void build(int wh,int l,int r){
		t[wh].l=l,t[wh].r=r;
		if(l==r)return;
		build(lc,l,mid);
		build(rc,mid+1,r);
		pushup(wh);
		return;
	}
	inline void change(int wh,int wl,int wr,int val){
		if(wl<=t[wh].l&&t[wh].r<=wr){
			pushnow(wh,val);
			return;
		}
		pushdown(wh);
		if(wl<=mid)change(lc,wl,wr,val);
		if(wr>mid)change(rc,wl,wr,val);
		pushup(wh);
		return; 
	}
	inline int work(int wh,int wl,int wr){
		if(wl<=t[wh].l&&t[wh].r<=wr){
			return t[wh].data;
		}
		pushdown(wh);
		int an=0;
		if(wl<=mid)an+=work(lc,wl,wr);
		if(wr>mid)an+=work(rc,wl,wr);
		return an%mod; 
	}
	inline int ask(int wh,int pl){
		if(t[wh].l==t[wh].r)return t[wh].data;
		pushdown(wh);
		if(pl<=mid)return ask(lc,pl);
		else return ask(rc,pl);
	}
}
//区间加下标平方的那棵树
namespace tc{
	struct node{
		int l,r,data,lazy;
	}t[N<<2];
	inline void pushup(int wh){
		t[wh].data=(t[lc].data+t[rc].data)%mod;
		return;
	}
	inline int s(int wh){
		return wh*(wh+1)/2*(2*wh+1)/3%mod;
	}
	inline void pushnow(int wh,int val){
		t[wh].lazy+=val;t[wh].lazy%=mod;
		t[wh].data+=val*(s(t[wh].r)-s(t[wh].l-1)+mod)%mod;
		t[wh].data%=mod;
		return;
	}
	inline void pushdown(int wh){
		if(t[wh].lazy){
			pushnow(lc,t[wh].lazy);
			pushnow(rc,t[wh].lazy);
			t[wh].lazy=0;
		}
		return;
	}
	inline void build(int wh,int l,int r){
		t[wh].l=l,t[wh].r=r;
		if(l==r)return;
		build(lc,l,mid);
		build(rc,mid+1,r);
		pushup(wh);
		return;
	}
	inline void change(int wh,int wl,int wr,int val){
		if(wl<=t[wh].l&&t[wh].r<=wr){
			pushnow(wh,val);
			return;
		}
		pushdown(wh);
		if(wl<=mid)change(lc,wl,wr,val);
		if(wr>mid)change(rc,wl,wr,val);
		pushup(wh);
		return; 
	}
	inline int work(int wh,int wl,int wr){
		if(wl<=t[wh].l&&t[wh].r<=wr){
			return t[wh].data;
		}
		pushdown(wh);
		int an=0;
		if(wl<=mid)an+=work(lc,wl,wr);
		if(wr>mid)an+=work(rc,wl,wr);
		return an%mod; 
	}
	inline int ask(int wh,int pl){
		if(t[wh].l==t[wh].r)return t[wh].data;
		pushdown(wh);
		if(pl<=mid)return ask(lc,pl);
		else return ask(rc,pl);
	}
} 
#undef lc
#undef rc
#undef mid

inline int solve(int l,int r){
	if(l==r){
		return (ta::ask(1,l)+(tb::ask(1,l)+tc::ask(1,l))%mod*yy%mod)%mod;
	}
	else{
		return (ta::work(1,l,r)+(tb::work(1,l,r)+tc::work(1,l,r))%mod*yy%mod)%mod;
	}
}

signed main(){
	
	#ifdef zczc
	freopen("a.in","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=1;i<=m;i++){read(a[i]);a[i]+=a[i-1];a[i]%=mod;}
	for(int i=1;i<=m;i++)a[i]+=a[i-1],a[i]%=mod;
	//for(int i=1;i<=m;i++)printf("%d ",a[i]);
	ta::build(1,0,m);
	tb::build(1,0,m);
	tc::build(1,0,m);
	int op,l,r,val;
	
	while(n--){
		read(op);read(l);read(r);
		if(l>r){int s1=l;l=r;r=s1;}
		if(op==2){
			int an=solve(m,m)*(r-l+1)-solve(l-1,r-1)-solve(m-r,m-l);
			printf("%lld\n",(an%mod+mod)%mod);
		}
		else{
			read(val);
			if(r<m){
				int len=r-l+1;
				ta::change(1,r+1,m,((val*len%mod*(len+1)%mod*yy%mod-val*len%mod*r%mod)%mod+mod)%mod);
				tb::change(1,r+1,m,2*val*len%mod);
			}
			ta::change(1,l,r,(l-1)*(l-2)%mod*val%mod*yy%mod);
			tb::change(1,l,r,val*(3-2*l)%mod);
			tc::change(1,l,r,val);
		}
	}
	
	return 0;
}
posted @ 2022-01-21 14:59  Feyn618  阅读(61)  评论(0)    收藏  举报