【線段樹】COGS 2632
阿新 • • 發佈:2018-12-30
參考部落格,作者: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); } } }