如題,已知乙個數列,你需要進行下面兩種操作:
將某區間每乙個數加上k。
求出某區間每乙個數的和。
第一行包含兩個整數n,m分別表示該數列數字的個數和操作的總個數。
第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。
接下來m行每行包含3或4個整數,表示乙個操作,具體如下:
1 x y k
:將區間[x,y]內的數每個加上k。
2 x y
:輸出區間[x,y]內每個數的和。
輸出包含若干行整數,即為所有操作 2 的結果。
輸入 #1複製
5 5輸出 #1複製1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
11保證任意時刻數列中任意元素的和在[-2^63,2^63)內。820
對於100%的資料,1<=n,m<=10^5。
【樣例解釋】
這是乙個經典的線段樹,曾經讓我滿臉懵逼的演算法,但是真的很好用。(雖然**有點長)今天我講講自己的理解,希望能幫到不會的同學。
一張爛到不能再爛的:
乙個神奇的操作:
void build(long long int l,long long int r,long long int k)這就是線段樹的初始化。大家可以輸出一下tree陣列的zhi變數,一定和上圖一樣,每個節點都等於他的兩個子節點。int mid=(l+r)/2;//分成2段,二分。
build(l,mid,k*2);//乙個位置是k*2
build(mid+1,r,k*2+1);//乙個位置是k*2+1
tree[k].zhi=tree[k*2].zhi+tree[k*2+1].zhi;//父節點的值相當於2個子節點的和。
}
線段樹初始化完了。接下來是查詢。
上面的初始化我們讓父節點等於他的2個子節點相加,我們就根據這個來求區間查詢。具體思想是:如果爸爸超過了範圍,就去找兒子,一直向下找,直到找到乙個被要求的區間完全包含的後代。然後就把他的值返回,這個方法是絕對不會重複的,因為線段樹每層每個節點值只包含在乙個空間內。如果爸爸被選擇,兒子也就沒有必要查下去了。就造就了乙個上下層不可能被選,同層不存在重疊的現象。所以這種方法不可能重複。
另外還有乙個小小的判斷,如果要選區間的開頭大於兒子的結尾,或者相反,那這個兒子就沒比要查下去了。
說了這麼多,該寫**了:
void chazhao(long long int k)//現在的位置查詢和建樹都是這麼草率。好好理解一下二分就可以寫出來。接下來是(我認為)最難的區間修改,他需要用到乙個神奇的東西,叫做懶標記,其意差不多是這個區間包含的值全都要加a,那我就先算出自己需要的值,加上。再定義乙個變數,告訴他這個以下全部都要+a,然後就不管了……咕咕咕int mid=(tree[k].l+tree[k].r)/2;//獲取子節點的結尾位置。
if(q<=mid)//開頭小於左子節點的結尾,左子節點包含一部分。需要檢視。
if(h>mid)//結尾大於右子節點的開頭,右子節點包含一部分。需要檢視。
}
當然沒這麼容易結束,我們以一種現在不用死活不動的態度來處理這個a。只有需要用到這個區間的子區間時,才會把標記下傳。懶標記的好處就是避免無用操作,用得到再動。可以毫不誇張的說,沒來懶標記的線段樹,連暴力都不如。
void down(long long int k)下降函式,當需要查詢乙個空間的子節點,但這個空間的懶標記沒有清空,就會對子節點的操作產生誤差。每個值都加上a的話,整個空間增加的量就是(存的長度*a)。然後這個空間需要繼承父親要增加的值。因為他的子節點一樣要加。但我們仍然以現在不用死活不動的態度來處理。也就是說,不主動向下傳,只有要用的時候再傳。
要判斷是否要用,就要在每個函式都加一些東西:
if(tree[k].f!=0)如果在查詢的時候不包含,就判斷。因為他要去下一層了,需要把這一層的懶標記向下移動。
現在就差最後一步,修改。
void xg(long long int k)好了,現在該上完整的**了。if(tree[k].f!=0)//要去找兒子,但懶標記還有,向下傳。
int mid=(tree[k].l+tree[k].r)/2;
if(q<=mid)
if(h>mid)
tree[k].zhi=tree[k*2].zhi+tree[k*2+1].zhi;//父節點的值等於左右子節點的和。
return;
}
#include#include#include#include#include#includeusing namespace std;long long n,shu,q,h,m,a,l,r,a1;
struct hehe
tree[400005];//陣列大小開到n*4比較保險
void build(long long int l,long long int r,long long int k)
int mid=(l+r)/2;//分成2段,二分。
build(l,mid,k*2);//乙個位置是k*2
build(mid+1,r,k*2+1);//乙個位置是k*2+1
tree[k].zhi=tree[k*2].zhi+tree[k*2+1].zhi;//父節點的值相當於2個子節點的和。
}void down(long long int k)
void chazhao(long long int k)//現在的位置
if(tree[k].f!=0)
int mid=(tree[k].l+tree[k].r)/2;//獲取子節點的結尾位置。
if(q<=mid)//開頭小於左子節點的結尾,左子節點包含一部分。需要檢視。
if(h>mid)//結尾大於右子節點的開頭,右子節點包含一部分。需要檢視。 }
void xg(long long int k)
if(tree[k].f!=0)
int mid=(tree[k].l+tree[k].r)/2;
if(q<=mid)
if(h>mid)
tree[k].zhi=tree[k*2].zhi+tree[k*2+1].zhi;
return;
}int main()
{ cin>>n>>m;
build(1,n,1);
for(int i=0;i今天的線段樹就先講到這裡,大家快去試試吧。
線段樹介紹(segment tree)
給定乙個區間 1,n 希望你實現一種資料結構,支援以下操作 1.修改 i 號節點的值。2.詢問區間 i,j 中所有節點的和。這不是樹狀陣列板子 3.修改區間 i,j 中所有節點的值 4.詢問 i 號節點的值 這不還是樹狀陣列板子 如果我要求乙個資料結構,同時滿足這四個要求。那樹狀陣列就不行了。樹狀陣...
線段樹簡單實現
首先,線段樹是一棵滿二叉樹。每個節點要麼有兩個孩子,要麼是深度相同的葉子節點 每個節點維護某個區間,根維護所有的。如圖,區間是二分父的區間。當有n個元素,初始化需要o n 時間,對區間操作需要o logn 時間。下面給出維護區間最小值的思路和 從下往上,每個節點的值為左右區間較小的那乙個即可。這算是...
簡單線段樹模板
入門第四天,前三天的沒時間補,回去再慢慢寫吧。今天學長講的是線段樹,講得很有老師的感覺.然後就是講的也都差不多聽懂了,只是有些細節在寫 的時候沒有注意到,一直錯。需要注意的點 1.在build的時候在left right時是node u a left 之前老是錯寫成node left a left ...