1. 程式人生 > >【線段樹】COGS 2632

【線段樹】COGS 2632

參考部落格,作者:dreaming__ldx,來源:CSDN


數列操作傳送門

【題目描述】

一個長度為n的序列,一開始序列數的權值都是0,有m次操作

支援兩種操作:

1 L R x,給區間[L,R]內位置為pos的數加上(pos-L)*x

0 L R,查詢區間[L,R]內的權值和

最終答案對109+7取模。

【輸入】

第一行兩個數n,m,表示序列長度和操作次數

接下來m行,每行描述一個操作,有如下兩種情況:

1 L R x,給區間[L,R]內位置為pos的數加上(pos−L)×x

0 L R,查詢區間[L,R]內的權值和

【輸出】

 

每一個0操作輸出一個整數模1e9+7

【樣例輸入】

5 5
0 2 3
1 4 5 1
1 1 5 5
0 1 4
0 2 3

【樣例輸出】

0
30
15 

【資料範圍】

對於30%的資料 n,m<=2000

對於100%的資料,n,m<=300000

保證讀入的都是非負整數,所有的x<=10000

 

【思路】 對於等差數列,注意到它是可以合併的。

比如兩個等長的等差數列。一個等差數列S1,第一項為A1,公差為t1,另一個等差數列S2,第一項為A2,公差為t2。

那麼把S1和S2加起來,就變成了一個第一項為(A1+A2),公差為(t1+t2)的新的等差數列。

現在只要我們知道等差數列的首項,公差和長度,就能求出數列的和。

那麼對於每個區間[L,R],用兩個標記維護一下這個區間的首項公差就行了。標記是可以合併的。

注意:這樣做的前提是標記具有可合併性。反例:區間加等比數列

求和就是(首+末)*項數 /2。把資料帶進去就是(首+(首+(項數-1)*公差))*(R-L+1) /2。

#include<bits/stdc++.h>
#define lc (root<<1)
#define rc (root<<1|1)
#define mid ((T[root].l+T[root].r)>>1)
#define ll long long
using namespace std;
const int maxn=3e5+5;
const ll mod=1e9+7;
int n,m,op,LL,RR,Val;
struct node{
	node(){l=r=sum=add=fir=0;}
	int l,r;
	ll sum,add,fir;
}T[maxn<<2];
inline void pushup(int root){T[root].sum=(T[lc].sum+T[rc].sum)%mod;}
inline void pushadd(int root,ll fir,ll v){
	(T[root].add+=v)%=mod,(T[root].fir+=fir)%=mod;
	(T[root].sum+=(T[root].r-T[root].l+1)*(2*fir+(T[root].r-T[root].l)*v)/2)%=mod;
}
inline void pushdown(int root){
	if(T[root].add){
		pushadd(lc,T[root].fir,T[root].add);
		pushadd(rc,T[root].fir+T[root].add*(mid-T[root].l+1),T[root].add);
		T[root].add=0,T[root].fir=0;
	}
}
void build(int root,int l,int r){
	T[root].l=l,T[root].r=r;
	if(l==r) return;
	build(lc,l,mid),build(rc,mid+1,r);
}
void update(int root,int L,int R,ll v){
	if(L<=T[root].l&&R>=T[root].r){
		pushadd(root,v*(T[root].l-LL),v);
		return;
	}
	pushdown(root);
	if(L<=mid) update(lc,L,R,v);
	if(R>mid) update(rc,L,R,v);
	pushup(root);
}
ll query(int root,int L,int R){
	if(L<=T[root].l&&R>=T[root].r) return T[root].sum;
	pushdown(root);
	ll ret=0;
	if(L<=mid) (ret+=query(lc,L,R))%=mod;
	if(R>mid) (ret+=query(rc,L,R))%=mod;
	return ret%mod;
}
inline void read(int &x){
	x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
int main(){
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);
	read(n),read(m);
	build(1,1,n);
	while(m--){
		read(op);
		if(op==1){
			read(LL),read(RR),read(Val);
			update(1,LL,RR,Val);
		}
		if(op==0){
			read(LL),read(RR);
			printf("%lld\n",query(1,LL,RR)%mod);
		}
	}
}