C 簡單介紹線段樹

2022-06-15 12:06:10 字數 3505 閱讀 3027

如題,已知乙個數列,你需要進行下面兩種操作:

將某區間每乙個數加上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 5 4 2 3

2 2 4

1 2 3 2

2 3 4

1 1 5 1

2 1 4

輸出 #1複製

11

820

保證任意時刻數列中任意元素的和在[-2^63,2^63)內。

對於100%的資料,1<=n,m<=10^5。

【樣例解釋】

這是乙個經典的線段樹,曾經讓我滿臉懵逼的演算法,但是真的很好用。(雖然**有點長)今天我講講自己的理解,希望能幫到不會的同學。

一張爛到不能再爛的:

乙個神奇的操作:

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個子節點的和。

}

這就是線段樹的初始化。大家可以輸出一下tree陣列的zhi變數,一定和上圖一樣,每個節點都等於他的兩個子節點。

線段樹初始化完了。接下來是查詢。

上面的初始化我們讓父節點等於他的2個子節點相加,我們就根據這個來求區間查詢。具體思想是:如果爸爸超過了範圍,就去找兒子,一直向下找,直到找到乙個被要求的區間完全包含的後代。然後就把他的值返回,這個方法是絕對不會重複的,因為線段樹每層每個節點值只包含在乙個空間內。如果爸爸被選擇,兒子也就沒有必要查下去了。就造就了乙個上下層不可能被選,同層不存在重疊的現象。所以這種方法不可能重複。

另外還有乙個小小的判斷,如果要選區間的開頭大於兒子的結尾,或者相反,那這個兒子就沒比要查下去了。

說了這麼多,該寫**了:

void chazhao(long long int k)//現在的位置

int mid=(tree[k].l+tree[k].r)/2;//獲取子節點的結尾位置。

if(q<=mid)//開頭小於左子節點的結尾,左子節點包含一部分。需要檢視。

if(h>mid)//結尾大於右子節點的開頭,右子節點包含一部分。需要檢視。

}

查詢和建樹都是這麼草率。好好理解一下二分就可以寫出來。接下來是(我認為)最難的區間修改,他需要用到乙個神奇的東西,叫做懶標記,其意差不多是這個區間包含的值全都要加a,那我就先算出自己需要的值,加上。再定義乙個變數,告訴他這個以下全部都要+a,然後就不管了……咕咕咕

當然沒這麼容易結束,我們以一種現在不用死活不動的態度來處理這個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 ...