線段樹是什么??線段樹怎么寫??
如果你在考提高組前一天還在問這個問題,那么你會與一等獎失之交臂;如果你還在沖擊普及組一等獎,那么這篇博客會浪費你人生中寶貴的5~20分鍾。
上面兩句話顯而易見,線段樹這個數據結構是一個從萌新到正式OI選手的過渡,是一個非常重要的算法,也是一個對於萌新來說較難的算法。不得不說,我學習了這個算法5遍左右才有勇氣寫的這篇博客。
但是,對於OI正式選手來說,線段樹不是算法,應該是一種工具。她能把一些對於區間(或者線段)的修改、維護,從O(N)的時間復雜度變成O(logN)。
廢話不說,這篇博客會分為四部:
第一部:線段樹概念引入
第二部:簡單(無pushdown)的線段樹
第三部:區間+/-修改與查詢
第四部:區間乘除修改與查詢
總結
第一部 概念引入
線段樹是一種二叉樹,也就是對於一個線段,我們會用一個二叉樹來表示。比如說一個長度為4的線段,我們可以表示成這樣:

這是什么意思呢? 如果你要表示線段的和,那么最上面的根節點的權值表示的是這個線段1~4的和。根的兩個兒子分別表示這個線段中1~2的和,與3~4的和。以此類推。
然后我們還可以的到一個性質:節點i的權值=她的左兒子權值+她的右兒子權值。因為1~4的和就是等於1~2的和+2~3的和。
根據這個思路,我們就可以建樹了,我們設一個結構體tree,tree[i].l和tree[i].r分別表示這個點代表的線段的左右下標,tree[i].sum表示這個節點表示的線段和。
我們知道,一顆二叉樹,她的左兒子和右兒子編號分別是她*2和她*2+1
再根據剛才的性質,得到式子:tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;就可以建一顆線段樹了!代碼如下:
inline void build(int i,int l,int r){//遞歸建樹 tree[i].l=l;tree[i].r=r; if(l==r){//如果這個節點是葉子節點 tree[i].sum=input[l]; return ; } int mid=(l+r)>>1; build(i*2,l,mid);//分別構造左子樹和右子樹 build(i*2+1,mid+1,r); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//剛才我們發現的性質return ; }
嗯,這就是線段樹的構建,你可能會問為什么要開好幾倍的內存去儲存一條線段。這是因為我們還沒有讓這個過大的數組干一些實事,那么什么是實事呢?讓我們進入下一部(在你看懂這一部的情況下)
第二部 簡單(無pushdown)的線段樹
1、單點修改,區間查詢
其實這一章開始才是真正的線段樹,我們要用線段樹干什么?答案是維護一個線段(或者區間),比如你想求出一個1~100區間中,4~67這些元素的和,你會怎么做?朴素的做法是for(i=4;i<=67;i++) sum+=a[i],這樣固然好,但是算得太慢了。
我們想一種新的方法,先想一個比較好畫圖的數據,比如一個長度為4的區間,分別是1、2、3、4,我們想求出第1~3項的和。按照上一部說的,我們要建出一顆線段樹,其中點權(也就是紅色)表示和:

然后我們要求1~3的和,我們先從根節點開始查詢,發現她的左兒子1~2這個區間和答案區間1~3有交集,那么我們跑到左兒子這個區間。
然后,我們發現這個區間1~2被完全包括在答案區間1~3這個區間里面,那就把她的值3返回。
我們回到了1~4區間,發現她的右兒子3~4區間和答案區間1~3有交集,那么我們走到3~4區間
到了3~4區間,我們發現她並沒有完全包含在答案區間1~3里面,但發現她的左兒子3~3區間和1~3區間又交集,那么久走到3~3區間
到了3~3區間,發現其被答案區間完全包含,就返回她的值3一直到最開始
3~3區間的3+1~2區間的3=6,我們知道了1~3區間和為6.
有人可能會說你這樣是不是瘋了,我那腳都能算出1+2+3=6,為什么這么麻煩?!
因為這才幾個數,如果一百萬個數,這樣時間會大大增快。
我們總結一下,線段樹的查詢方法:
1、如果這個區間被完全包括在目標區間里面,直接返回這個區間的值
2、如果這個區間的左兒子和目標區間有交集,那么搜索左兒子
3、如果這個區間的右兒子和目標區間有交集,那么搜索右兒子
寫成代碼,就會變成這樣:
inline int search(int i,int l,int r){ if(tree[i].l>=l && tree[i].r<=r)//如果這個區間被完全包括在目標區間里面,直接返回這個區間的值 return tree[i].sum; if(tree[i].r<l || tree[i].l>r) return 0;//如果這個區間和目標區間毫不相干,返回0 int s=0; if(tree[i*2].r>=l) s+=search(i*2,l,r);//如果這個區間的左兒子和目標區間又交集,那么搜索左兒子 if(tree[i*2+1].l<=r) s+=search(i*2+1,l,r);//如果這個區間的右兒子和目標區間又交集,那么搜索右兒子 return s; }
關於那幾個if的條件一定要看清楚,最好背下來,以防考場上腦抽推錯。
然后,我們怎么修改這個區間的單點,其實這個相對簡單很多,你要把區間的第dis位加上k。
那么你從根節點開始,看這個dis是在左兒子還是在右兒子,在哪往哪跑,
然后返回的時候,還是按照tree[i].sum=tree[i*2].sum+tree[i*2+1].sum的原則,更新所有路過的點
如果不理解,我還是畫個圖吧,其中深藍色是去的路徑,淺藍色是返回的路徑,回來時候紅色的+標記就是把這個點加上這個值。

把這個過程變成代碼,就是這個樣子:
inline void add(int i,int dis,int k){ if(tree[i].l==tree[i].r){//如果是葉子節點,那么說明找到了 tree[i].sum+=k; return ; } if(dis<=tree[i*2].r) add(i*2,dis,k);//在哪往哪跑 else add(i*2+1,dis,k); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//返回更新 return ; }
2、區間修改,單點查詢
區間修改和單點查詢,我們的思路就變為:如果把這個區間加上k,相當於把這個區間塗上一個k的標記,然后單點查詢的時候,就從上跑道下,把沿路的標記加起來就好。
這里面給區間貼標記的方式與上面的區間查找類似,原則還是那三條,只不過第一條:如果這個區間被完全包括在目標區間里面,直接返回這個區間的值變為了如果這個區間如果這個區間被完全包括在目標區間里面,講這個區間標記k
具體做法很像,這里貼上代碼:
inline void add(int i,int l,int r,int k){ if(tree[i].l>=l && tree[i].r<=r){//如果這個區間被完全包括在目標區間里面,講這個區間標記k tree[i].sum+=k; return ; } if(tree[i*2].r>=l) add(i*2,l,r,k); if(tree[i*2+1].l<=r) add(i*2+1,l,r,k); }
然后就是單點查詢了,這個更好理解了,就是dis在哪往哪跑,把路徑上所有的標價加上就好了:
void search(int i,int dis){ ans+=tree[i].num;//一路加起來 if(tree[i].l==tree[i].r) return ; if(dis<=tree[i*2].r) search(i*2,dis); if(dis>=tree[i*2+1].l) search(i*2+1,dis); }
不知不覺,這第二章已經結束。這樣的簡單(原諒我用這個詞)線段樹,還可除了求和,還可以求區間最小最大值,還可以區間染色。
但是!這樣的線段樹展現不出來她的魅力,因為區間求和,樹狀數組比她少了一個很大的常熟。二區間最值,ST的那神乎其技的O(n)查詢也能完爆她。這是為什么?因為線段樹的魅力還沒有展現出來,她最美麗的地方:pushdown還未展現於世,如果你已經對這一章充足的了解,並且能不看博客把洛谷上樹狀數組模板1、2都能寫出來,那么請你進入下一部。
第三部 進階線段樹
區間修改、區間查詢,你可能會認為,把上一章里面的這兩個模塊加在一起就好了,然后你就會發現你大錯特錯。
因為如果對於1~4這個區間,你把1~3區間+1,相當於把節點1~2和3標記,但是如果你查詢2~4時,你會發現你加的時沒有標記的2節點和沒有標記的3~4節點加上去,結果當然是錯的。
那么我們應該怎么辦?這時候pushdown的作用就顯現出來了。
你會想到,我們只需要在查詢的時候,如果我們要查的2節點在1~2區間的里面,那我們就可以把1~2區間標記的那個+1給推下去這樣就能順利地加上了。
怎么記錄這個標記呢?我們需要記錄一個“懶標記”lazytage,來記錄這個區間
區間修改的時候,我們按照如下原則:
1、如果當前區間被完全覆蓋在目標區間里,講這個區間的sum+k*(tree[i].r-tree[i].l+1)
2、如果沒有完全覆蓋,則先下傳懶標記
3、如果這個區間的左兒子和目標區間有交集,那么搜索左兒子
4、如果這個區間的右兒子和目標區間有交集,那么搜索右兒子
然后查詢的時候,將這個懶標記下傳就好了,下面圖解一下:
如圖,區間1~4分別是1、2、3、4,我們要把1~3區間+1。因為1~2區間被完全覆蓋,所以將其+2,並將紫色的lazytage+1,3區間同理

注意我們處理完這些以后,還是要按照tree[i].sum=tree[i*2].sum+tree[i*2+1].sum的原則返回,代碼如下:
void add(int i,int l,int r,int k) { if(tree[i].r<=r && tree[i].l>=l)//如果當前區間被完全覆蓋在目標區間里,講這個區間的sum+k*(tree[i].r-tree[i].l+1) { tree[i].sum+=k*(tree[i].r-tree[i].l+1); tree[i].lz+=k;//記錄lazytage return ; } push_down(i);//向下傳遞 if(tree[i*2].r>=l) add(i*2,l,r,k); if(tree[i*2+1].l<=r) add(i*2+1,l,r,k); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; return ; }
其中的pushdown,就是把自己的lazytage歸零,並給自己的兒子加上,並讓自己的兒子加上k*(r-l+1)
void push_down(int i) { if(tree[i].lz!=0) { tree[i*2].lz+=tree[i].lz;//左右兒子分別加上父親的lz tree[i*2+1].lz+=tree[i].lz; init mid=(tree[i].l+tree[i].r)/2; tree[i*2].data+=tree[i].lz*(mid-tree[i*2].l+1);//左右分別求和加起來 tree[i*2+1].data+=tree[i].lz*(tree[i*2+1].r-mid); tree[i].lz=0;//父親lz歸零 } return ; }
查詢的時候,和上一章的幾乎一樣,就是也要像修改一樣加入pushdown,這里用圖模擬一下。我們要查詢2~4區間的和,這是查詢前的情況,所有紫色的代表lazytage

然后,我們查到區間1~2時,發現這個區間並沒有被完全包括在目標區間里,於是我們就pushdown,lazytage下傳,並讓每個區間sum加上(r-l)*lazytage。

然后查到2~2區間,發現被完全包含,所以就返3,再搜索到3~4區間,發現被完全包含,那么直接返回8,最后3+8=11就是答案
這里是代碼實現:
inline int search(int i,int l,int r){ if(tree[i].l>=l && tree[i].r<=r) return tree[i].sum; if(tree[i].r<l || tree[i].l>r) return 0; push_down(i); int s=0; if(tree[i*2].r>=l) s+=search(i*2,l,r); if(tree[i*2+1].l<=r) s+=search(i*2+1,l,r); return s; }
好了,到了這里,我們就學會了用線段樹進行區間加減操作,大家可以完成洛谷的線段樹模板1.
第四部 乘法(根號)線段樹
1、乘法線段樹
如果這個線段樹只有乘法,那么直接加入lazytage變成乘,然后tree[i].sum*=k就好了。但是,如果我們是又加又乘,那就不一樣了。
當lazytage下標傳遞的時候,我們需要考慮,是先加再乘還是先乘再加。我們只需要對lazytage做這樣一個處理。
lazytage分為兩種,分別是加法的plz和乘法的mlz。
mlz很簡單處理,pushdown時直接*父親的就可以了,那么加法呢?
我們需要把原先的plz*父親的mlz再加上父親的plz.
inline void pushdown(long long i){//注意這種級別的數據一定要開long long long long k1=tree[i].mlz,k2=tree[i].plz; tree[i<<1].sum=(tree[i<<1].sum*k1+k2*(tree[i<<1].r-tree[i<<1].l+1))%p;// tree[i<<1|1].sum=(tree[i<<1|1].sum*k1+k2*(tree[i<<1|1].r-tree[i<<1|1].l+1))%p; tree[i<<1].mlz=(tree[i<<1].mlz*k1)%p; tree[i<<1|1].mlz=(tree[i<<1|1].mlz*k1)%p; tree[i<<1].plz=(tree[i<<1].plz*k1+k2)%p; tree[i<<1|1].plz=(tree[i<<1|1].plz*k1+k2)%p; tree[i].plz=0; tree[i].mlz=1; return ; }
然后加法和減法的函數同理,維護lazytage的時候加法標記一定要記得現乘再加。
值得一提的是,計算*2時一定要改成i<<1這樣能解決很多時間,還有要開long long,還有,函數前面要加inline 我在其他OJ交這道題時,就因為沒加inline 就被卡了,交了就過了。
2、根號線段樹
其實,根號線段樹和除法線段樹一樣。她們乍眼一看感覺直接用lazytage標記除了多少,但是實際上,會出現精度問題。
c++的除法是向下取整,很明顯,(a+b)/k!=a/k+b/k(在向下取整的情況下),而根號,很明顯根號(a)+根號(b)!=根號(a+b)那么怎么辦?
第一個想法就是暴力,對於每個要改動的區間l~r,把里面的每個點都單獨除,但這樣就會把時間復雜度卡得比大暴力都慢(因為多個常數),所以怎么優化?
我們對於每個區間,維護她的最大值和最小值,然后每次修改時,如果這個區間的最大值根號和最小值的根號一樣,說明這個區間整體根號不會產生誤差,就直接修改(除法同理)
其中,lazytage把除法當成減法,記錄的是這個區間里每個元素減去的值。
下面是根號線段樹的修改過程:
inline void Sqrt(int i,int l,int r){ if(tree[i].l>=l && tree[i].r<=r && (tree[i].minn-(long long)sqrt(tree[i].minn))==(tree[i].maxx-(long long)sqrt(tree[i].maxx))){//如果這個區間的最大值最小值一樣 long long u=tree[i].minn-(long long)sqrt(tree[i].minn);//計算區間中每個元素需要減去的 tree[i].lz+=u; tree[i].sum-=(tree[i].r-tree[i].l+1)*u; tree[i].minn-=u; tree[i].maxx-=u; //cout<<"i"<<i<<" "<<tree[i].sum<<endl; return ; } if(tree[i].r<l || tree[i].l>r) return ; push_down(i); if(tree[i*2].r>=l) Sqrt(i*2,l,r); if(tree[i*2+1].l<=r) Sqrt(i*2+1,l,r); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; tree[i].minn=min(tree[i*2].minn,tree[i*2+1].minn);//維護最大值和最小值 tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx); //cout<<"i"<<i<<" "<<tree[i].sum<<endl; return ; }
然后pushdown沒什么變化,就是要記得tree[i].minn、tree[i].maxx也要記得-lazytage。
模板題與代碼:
最后,我們給幾道模板題,再貼上代碼:
單點修改,區間查詢:洛谷樹狀數組模板1
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <cstdlib> #include <queue> #include <stack> #include <vector> using namespace std; #define MAXN 100010 #define INF 10000009 #define MOD 10000007 #define LL long long #define in(a) a=read() #define REP(i,k,n) for(long long i=k;i<=n;i++) #define DREP(i,k,n) for(long long i=k;i>=n;i--) #define cl(a) memset(a,0,sizeof(a)) inline long long read(){ long long x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void out(long long x){ if(x<0) putchar('-'),x=-x; if(x>9) out(x/10); putchar(x%10+'0'); } long long n,m,p; long long input[MAXN]; struct node{ long long l,r; long long sum,mlz,plz; }tree[4*MAXN]; inline void build(long long i,long long l,long long r){ tree[i].l=l; tree[i].r=r; tree[i].mlz=1; if(l==r){ tree[i].sum=input[l]%p; return ; } long long mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p; return ; } inline void pushdown(long long i){ long long k1=tree[i].mlz,k2=tree[i].plz; tree[i<<1].sum=(tree[i<<1].sum*k1+k2*(tree[i<<1].r-tree[i<<1].l+1))%p; tree[i<<1|1].sum=(tree[i<<1|1].sum*k1+k2*(tree[i<<1|1].r-tree[i<<1|1].l+1))%p; tree[i<<1].mlz=(tree[i<<1].mlz*k1)%p; tree[i<<1|1].mlz=(tree[i<<1|1].mlz*k1)%p; tree[i<<1].plz=(tree[i<<1].plz*k1+k2)%p; tree[i<<1|1].plz=(tree[i<<1|1].plz*k1+k2)%p; tree[i].plz=0; tree[i].mlz=1; return ; } inline void mul(long long i,long long l,long long r,long long k){ if(tree[i].r<l || tree[i].l>r) return ; if(tree[i].l>=l && tree[i].r<=r){ tree[i].sum=(tree[i].sum*k)%p; tree[i].mlz=(tree[i].mlz*k)%p; tree[i].plz=(tree[i].plz*k)%p; return ; } pushdown(i); if(tree[i<<1].r>=l) mul(i<<1,l,r,k); if(tree[i<<1|1].l<=r) mul(i<<1|1,l,r,k); tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p; return ; } inline void add(long long i,long long l,long long r,long long k){ if(tree[i].r<l || tree[i].l>r) return ; if(tree[i].l>=l && tree[i].r<=r){ tree[i].sum+=((tree[i].r-tree[i].l+1)*k)%p; tree[i].plz=(tree[i].plz+k)%p; return ; } pushdown(i); if(tree[i<<1].r>=l) add(i<<1,l,r,k); if(tree[i<<1|1].l<=r) add(i<<1|1,l,r,k); tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p; return ; } inline long long search(long long i,long long l,long long r){ if(tree[i].r<l || tree[i].l>r) return 0; if(tree[i].l>=l && tree[i].r<=r) return tree[i].sum; pushdown(i); long long sum=0; if(tree[i<<1].r>=l) sum+=search(i<<1,l,r)%p; if(tree[i<<1|1].l<=r) sum+=search(i<<1|1,l,r)%p; return sum%p; } int main(){ in(n); in(m);in(p); REP(i,1,n) in(input[i]); build(1,1,n); REP(i,1,m){ long long fl,a,b,c; in(fl); if(fl==1){ in(a);in(b);in(c); c%=p; mul(1,a,b,c); } if(fl==2){ in(a);in(b);in(c); c%=p; add(1,a,b,c); } if(fl==3){ in(a);in(b); printf("%lld\n",search(1,a,b)); } } return 0; } /* 5 4 1000 1 2 3 4 5 3 1 5 2 1 5 1 1 1 5 2 3 1 5 */
區間修改,單點查詢:洛谷樹狀數組模板2
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cmath> #include <queue> using namespace std; int n,m; int ans; int input[500010]; struct node { int left,right; int num; }tree[2000010]; void build(int left,int right,int index) { tree[index].num=0; tree[index].left=left; tree[index].right=right; if(left==right) return ; int mid=(right+left)/2; build(left,mid,index*2); build(mid+1,right,index*2+1); } void pls(int index,int l,int r,int k) { if(tree[index].left>=l && tree[index].right<=r) { tree[index].num+=k; return ; } if(tree[index*2].right>=l) pls(index*2,l,r,k); if(tree[index*2+1].left<=r) pls(index*2+1,l,r,k); } void search(int index,int dis) { ans+=tree[index].num; if(tree[index].left==tree[index].right) return ; if(dis<=tree[index*2].right) search(index*2,dis); if(dis>=tree[index*2+1].left) search(index*2+1,dis); } int main() { int n,m; cin>>n>>m; build(1,n,1); for(int i=1;i<=n;i++) scanf("%d",&input[i]); for(int i=1;i<=m;i++) { int a; scanf("%d",&a); if(a==1) { int x,y,z; scanf("%d%d%d",&x,&y,&z); pls(1,x,y,z); } if(a==2) { ans=0; int x; scanf("%d",&x); search(1,x); printf("%d\n",ans+input[x]); } } }
區間加法,洛谷線段樹模板1
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #define init long long using namespace std; init n,m; struct node { init l,r,data; init lt; }tree[1000010]; init arr[1000010]; void build(init l,init r,init index,init arr[]) { tree[index].lt=0; tree[index].l=l; tree[index].r=r; if(l==r) { tree[index].data=arr[l]; return ; } init mid=(l+r)/2; build(l,mid,index*2,arr); build(mid+1,r,index*2+1,arr); tree[index].data=tree[index*2].data+tree[index*2+1].data; return ; } void push_down(init index) { if(tree[index].lt!=0) { tree[index*2].lt+=tree[index].lt; tree[index*2+1].lt+=tree[index].lt; init mid=(tree[index].l+tree[index].r)/2; tree[index*2].data+=tree[index].lt*(mid-tree[index*2].l+1); tree[index*2+1].data+=tree[index].lt*(tree[index*2+1].r-mid); tree[index].lt=0; } return ; } void up_data(init index,init l,init r,init k) { if(tree[index].r<=r && tree[index].l>=l) { tree[index].data+=k*(tree[index].r-tree[index].l+1); tree[index].lt+=k; return ; } push_down(index); if(tree[index*2].r>=l) up_data(index*2,l,r,k); if(tree[index*2+1].l<=r) up_data(index*2+1,l,r,k); tree[index].data=tree[index*2].data+tree[index*2+1].data; return ; } init search(init index,init l,init r) { if(tree[index].l>=l && tree[index].r<=r) return tree[index].data; push_down(index); init num=0; if(tree[index*2].r>=l) num+=search(index*2,l,r); if(tree[index*2+1].l<=r) num+=search(index*2+1,l,r); return num; } int main() { cin>>n>>m; for(init i=1;i<=n;i++) cin>>arr[i]; build(1,n,1,arr); for(init i=1;i<=m;i++) { init f; cin>>f; if(f==1) { init a,b,c; cin>>a>>b>>c; up_data(1,a,b,c); } if(f==2) { init a,b; cin>>a>>b; printf("%lld\n",search(1,a,b)); } } }
區間乘法:洛谷線段樹模板2
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <cstdlib> #include <queue> #include <stack> #include <vector> using namespace std; #define MAXN 100010 #define INF 10000009 #define MOD 10000007 #define LL long long #define in(a) a=read() #define REP(i,k,n) for(long long i=k;i<=n;i++) #define DREP(i,k,n) for(long long i=k;i>=n;i--) #define cl(a) memset(a,0,sizeof(a)) inline long long read(){ long long x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void out(long long x){ if(x<0) putchar('-'),x=-x; if(x>9) out(x/10); putchar(x%10+'0'); } long long n,m,p; long long input[MAXN]; struct node{ long long l,r; long long sum,mlz,plz; }tree[4*MAXN]; inline void build(long long i,long long l,long long r){ tree[i].l=l; tree[i].r=r; tree[i].mlz=1; if(l==r){ tree[i].sum=input[l]%p; return ; } long long mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p; return ; } inline void pushdown(long long i){ long long k1=tree[i].mlz,k2=tree[i].plz; tree[i<<1].sum=(tree[i<<1].sum*k1+k2*(tree[i<<1].r-tree[i<<1].l+1))%p; tree[i<<1|1].sum=(tree[i<<1|1].sum*k1+k2*(tree[i<<1|1].r-tree[i<<1|1].l+1))%p; tree[i<<1].mlz=(tree[i<<1].mlz*k1)%p; tree[i<<1|1].mlz=(tree[i<<1|1].mlz*k1)%p; tree[i<<1].plz=(tree[i<<1].plz*k1+k2)%p; tree[i<<1|1].plz=(tree[i<<1|1].plz*k1+k2)%p; tree[i].plz=0; tree[i].mlz=1; return ; } inline void mul(long long i,long long l,long long r,long long k){ if(tree[i].r<l || tree[i].l>r) return ; if(tree[i].l>=l && tree[i].r<=r){ tree[i].sum=(tree[i].sum*k)%p; tree[i].mlz=(tree[i].mlz*k)%p; tree[i].plz=(tree[i].plz*k)%p; return ; } pushdown(i); if(tree[i<<1].r>=l) mul(i<<1,l,r,k); if(tree[i<<1|1].l<=r) mul(i<<1|1,l,r,k); tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p; return ; } inline void add(long long i,long long l,long long r,long long k){ if(tree[i].r<l || tree[i].l>r) return ; if(tree[i].l>=l && tree[i].r<=r){ tree[i].sum+=((tree[i].r-tree[i].l+1)*k)%p; tree[i].plz=(tree[i].plz+k)%p; return ; } pushdown(i); if(tree[i<<1].r>=l) add(i<<1,l,r,k); if(tree[i<<1|1].l<=r) add(i<<1|1,l,r,k); tree[i].sum=(tree[i<<1].sum+tree[i<<1|1].sum)%p; return ; } inline long long search(long long i,long long l,long long r){ if(tree[i].r<l || tree[i].l>r) return 0; if(tree[i].l>=l && tree[i].r<=r) return tree[i].sum; pushdown(i); long long sum=0; if(tree[i<<1].r>=l) sum+=search(i<<1,l,r)%p; if(tree[i<<1|1].l<=r) sum+=search(i<<1|1,l,r)%p; return sum%p; } int main(){ in(n); in(m);in(p); REP(i,1,n) in(input[i]); build(1,1,n); REP(i,1,m){ long long fl,a,b,c; in(fl); if(fl==1){ in(a);in(b);in(c); c%=p; mul(1,a,b,c); } if(fl==2){ in(a);in(b);in(c); c%=p; add(1,a,b,c); } if(fl==3){ in(a);in(b); printf("%lld\n",search(1,a,b)); } } return 0; } /* 5 4 1000 1 2 3 4 5 3 1 5 2 1 5 1 1 1 5 2 3 1 5 */
區間根號,bzoj3211
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #define MAXN 1000010 #define REP(i,k,n) for(int i=k;i<=n;i++) #define in(a) a=read() using namespace std; int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } struct node{ int l,r; long long lz,sum,maxx,minn; }tree[MAXN<<2]; int n,m,input[MAXN]; inline void build(int i,int l,int r){ tree[i].l=l;tree[i].r=r; if(l==r){ tree[i].sum=tree[i].minn=tree[i].maxx=input[l]; return ; } int mid=(l+r)>>1; build(i*2,l,mid); build(i*2+1,mid+1,r); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; tree[i].minn=min(tree[i*2].minn,tree[i*2+1].minn); tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx); return ; } inline void push_down(int i){ if(!tree[i].lz) return ; long long k=tree[i].lz; tree[i*2].lz+=k; tree[i*2+1].lz+=k; tree[i*2].sum-=(tree[i*2].r-tree[i*2].l+1)*k; tree[i*2+1].sum-=(tree[i*2+1].r-tree[i*2+1].l+1)*k; tree[i*2].minn-=k; tree[i*2+1].minn-=k; tree[i*2].maxx-=k; tree[i*2+1].maxx-=k; tree[i].lz=0; return ; } inline void Sqrt(int i,int l,int r){ if(tree[i].l>=l && tree[i].r<=r && (tree[i].minn-(long long)sqrt(tree[i].minn))==(tree[i].maxx-(long long)sqrt(tree[i].maxx))){ long long u=tree[i].minn-(long long)sqrt(tree[i].minn); tree[i].lz+=u; tree[i].sum-=(tree[i].r-tree[i].l+1)*u; tree[i].minn-=u; tree[i].maxx-=u; //cout<<"i"<<i<<" "<<tree[i].sum<<endl; return ; } if(tree[i].r<l || tree[i].l>r) return ; push_down(i); if(tree[i*2].r>=l) Sqrt(i*2,l,r); if(tree[i*2+1].l<=r) Sqrt(i*2+1,l,r); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; tree[i].minn=min(tree[i*2].minn,tree[i*2+1].minn); tree[i].maxx=max(tree[i*2].maxx,tree[i*2+1].maxx); //cout<<"i"<<i<<" "<<tree[i].sum<<endl; return ; } inline long long search(int i,int l,int r){ if(tree[i].l>=l && tree[i].r<=r) return tree[i].sum; if(tree[i].r<l || tree[i].l>r) return 0; push_down(i); long long s=0; if(tree[i*2].r>=l) s+=search(i*2,l,r); if(tree[i*2+1].l<=r) s+=search(i*2+1,l,r); return s; } int main(){ in(n); REP(i,1,n) in(input[i]); build(1,1,n); in(m); int a,b,c; REP(i,1,m){ in(a);in(b);in(c); if(a==1) printf("%lld\n",search(1,b,c)); if(a==2){ Sqrt(1,b,c); //for(int i=1;i<=7;i++) // cout<<tree[i].sum<<" "; // cout<<endl; } } }
OI路漫漫,學無止境,願線段樹陪你走過半生,歸來仍是少年。
