BJOI2018链上二次求和——线段树

BJOI2018 链上二次求和

思路

[l,r]的限制可以拆成[1,l-1],[1,r],然后考虑推式子。

\(s_i\)\(a_i\)的前缀和。

\({\rm ans}=\sum_{i=1}^{x}\sum_{j=i}^{n}s_{j}-s_{j-i}\)

\(t_i\)\(s_i\)的前缀和。

\({\rm ans}=\sum_{i=1}^{x}t_{n}-t_{i-1}-t_{n-i}\)

发现如果我们可以区间维护前缀和的前缀和,那么就可以直接区间查询得到答案了。

考虑一次修改对答案造成的影响,发现对于下标为\(i\)的点的贡献为一个\(ai^{2}+bi+c\)形式的多项式,也就是区间加上一个二次函数。

对于区间加上二次函数,我们可以通过简单的线段树打标记来完成,修改一个点时需要用到二次幂和,直接套用公示即可,标记合并直接相加即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.3.31
 * Problem : loj2512
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
	freopen("loj2512.in","r",stdin);
	freopen("loj2512.out","w",stdout);
}

template<typename T>void read(T &_){
	_=0; T f=1; char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
	_*=f;
}

string proc(){
	ifstream f("/proc/self/status");
	return string(istreambuf_iterator<char>(f),istreambuf_iterator<char>());
}

const int maxn=2e5+10;
const int mod=1e9+7;
int n,q,s[maxn];

void mul(int &x,int y){
	x=1ll*x*y%mod;
}
void inc(int &x,int y){
	x+=y;
	if(x>=mod)x-=mod;
	else if(x<0)x+=mod;
}

int qpow(int x,int y=mod-2){
	int ret=1; x%=mod;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return ret;
}

const int inv2=qpow(2);
const int inv6=qpow(6);

#define mid ((l+r)>>1)
#define lc o<<1
#define rc o<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
int sum[maxn<<2],a[maxn<<2],b[maxn<<2],c[maxn<<2];

void build(int o,int l,int r){
	if(l==r)sum[o]=s[l];
	else{
		build(lson),build(rson);
		sum[o]=(sum[lc]+sum[rc])%mod;
	}
}

int S2(int x){return 1ll*x*(x+1)%mod*(2*x+1)%mod*inv6%mod;}
int S1(int x){return 1ll*x*(x+1)%mod*inv2%mod;}

void pushdown(int o,int l,int r){
	if(a[o]){
		inc(sum[lc],1ll*(S2(mid)-S2(l-1))*a[o]%mod),inc(a[lc],a[o]);
		inc(sum[rc],1ll*(S2(r)-S2(mid))*a[o]%mod),inc(a[rc],a[o]);
	}
	if(b[o]){
		inc(sum[lc],1ll*(S1(mid)-S1(l-1))*b[o]%mod),inc(b[lc],b[o]);
		inc(sum[rc],1ll*(S1(r)-S1(mid))*b[o]%mod),inc(b[rc],b[o]);
	}
	if(c[o]){
		inc(sum[lc],1ll*(mid-l+1)*c[o]%mod),inc(c[lc],c[o]);
		inc(sum[rc],1ll*(r-mid)*c[o]%mod),inc(c[rc],c[o]);
	}
	a[o]=b[o]=c[o]=0;
}

void update(int o,int l,int r,int L,int R,int aa,int bb,int cc){
	if(L>R)return;
	if(L<=l && r<=R){
		//debug(l),debug(r)<<endl;
		//printf("%d %d %d\n",aa,bb,cc);
		//debug(sum[o])<<endl;
		inc(sum[o],1ll*(S2(r)-S2(l-1))*aa%mod),inc(a[o],aa);
		inc(sum[o],1ll*(S1(r)-S1(l-1))*bb%mod),inc(b[o],bb);
		inc(sum[o],1ll*(r-l+1)*cc%mod),inc(c[o],cc);
		//debug(sum[o])<<endl;
	}
	else{
		pushdown(o,l,r);
		if(L<=mid)update(lson,L,R,aa,bb,cc);
		if(R>=mid+1)update(rson,L,R,aa,bb,cc);
		sum[o]=(sum[lc]+sum[rc])%mod;
	}
}

int query(int o,int l,int r,int L,int R){
	if(L>R)return 0;
	if(L<=l && r<=R)return sum[o];
	int ret=0;
	pushdown(o,l,r);
	if(L<=mid)inc(ret,query(lson,L,R));
	if(R>=mid+1)inc(ret,query(rson,L,R));
	return ret;
}

int solve(int x){
	return (1ll*query(1,1,n,n,n)*x%mod-query(1,1,n,1,x-1)-query(1,1,n,n-x,n-1))%mod;
}

int main(){
	File();

	read(n),read(q);
	REP(i,1,n)read(s[i]);
	REP(i,1,n)inc(s[i],s[i-1]);
	REP(i,1,n)inc(s[i],s[i-1]);
	build(1,1,n);

	int ty,l,r,x;
	REP(i,1,q){
		read(ty);
		if(ty==1){
			read(l),read(r),read(x);
			if(l>r)swap(l,r);
			update(1,1,n,l,r,1ll*inv2*x%mod,1ll*(3-2*l)*inv2%mod*x%mod,1ll*(2-l)*(1-l)%mod*inv2%mod*x%mod);
			update(1,1,n,r+1,n,0,1ll*(r-l+1)*x%mod,1ll*(2-l-r)*(r-l+1)%mod*inv2%mod*x%mod);
		}
		else{
			read(l),read(r);
			printf("%d\n",((solve(r)-solve(l-1))%mod+mod)%mod);
		}
		/*REP(j,1,n)printf("%d ",query(1,1,n,j,j));
		printf("\n");*/
	}

	return 0;
}

posted @ 2019-03-31 22:19  ylsoi  阅读(442)  评论(0编辑  收藏  举报