線段樹和zkw線段樹

2022-05-24 09:03:11 字數 4349 閱讀 5125

好啦,我們就開始說說線段樹吧

線段樹是個支援區間操作和查詢的東東,平時的話還是蠻實用的

下面以最基本的區間加以及查詢區間和為例

線段樹顧名思義就是棵樹嘛,葉子節點是每個基本點,它們所對應的父親就是它們的和,具體如下圖

但是對於這樣的線段樹來說,操作所需的時間是遠達不到我們的要求的(會被t),因為我們會進行一些不必要的操作,就像如果沒有查詢到某個點,那麼就沒有必要去修改這個點的值,為此,我們會引入乙個懶標記,記錄每個基本點需要被加上的值(稱為add),那麼樹上任意乙個點需要增加的值=該點對應的區間長度*add

那麼總的來說,線段樹的基本操作我個人認為可以分成3個,建樹、修改和查詢,當然如果繼續細分也是口以(可以)的,就比如說還可以分出 區間和的向上傳遞(父親節點等於子節點的和)和懶標記的向下傳遞(子節點的懶標記=原來的懶標記+父節點的懶標記)

所以接下來我們就來看看建樹、修改和查詢這3部分的具體**吧(深呼吸)

首先是建樹(build)

#define ls 2*rt,l,(l+r)/2                //

left son

#define rs 2*rt+1,(l+r)/2+1,r //

right son

#define ll long long

void build(ll rt,ll l,ll r)//

rt是當前點,l和r代表l到r區間的和

else

//否則就去看看當前點的左右兒子

return;}

//一層一層的求,我們就可以建好乙個初步的樹啦

然後是修改(change)

#define ls 2*rt,l,(l+r)/2          //

左右兒子,和之前一樣

#define rs 2*rt+1,(l+r)/2+1,r

#define ll long long

void

change(ll rt,ll l,ll r,ll l,ll r,ll add)

//當前點,當前區間的左右端點,需要修改的區間的左右端點,需要給每個基本點加上的值

if(o[rt]!=0)//

如果說我們恰好經過了乙個被打上懶標記的點,那不如就順手把它的懶標記下傳好了

if(l<=(l+r)/2

)//二分思想,

如果需要修改的區間左端點在當前區間中點的左邊,即當前區間中點左側有需要修改的點的話

if(r>(l+r)/2)//

同理 su[rt]=su[2*rt]+su[2*rt+1];//橘氏春秋有云(什麼鬼):

有下就有上,改完記得上傳

return

;}

呼啊,已經完成2/3了,堅持就是勝利!↖(^ω^)↗

查詢(find)

void

find(ll rt,ll l,ll r,ll l,ll r)

//當前點,當前區間左右端點,需要查詢的區間左右端點

else

//不然就找找它應該在那個區間裡面

if(l<=(l+r)/2)//

二分思想,如果左邊有點

if(r>(l+r)/2)//

如果右邊有點

su[rt]=su[2*rt]+su[2*rt+1];//

還是那句老話,橘氏春秋有云:有下就有上

}

return;//

看到return我就開心↖(^ω^)↗

}

哇吼,結束了才怪,接下來是總**!

//

線段樹要寫成lazy[i]+=lazy[祖先]的形式

//#include#include

#define ls 2*rt,l,(l+r)/2

#define rs 2*rt+1,(l+r)/2+1,r

#define ll long long

using

namespace

std;

intn,m,a,c;

ll su[

400005],x,y,k,ans[100005],o[400005];//

陣列開4倍

void

build(ll rt,ll l,ll r)

else

return;}

void

change(ll rt,ll l,ll r,ll l,ll r,ll add)

if(o[rt]!=0

)

if(l<=(l+r)/2

)

if(r>(l+r)/2

)

su[rt]=su[2*rt]+su[2*rt+1

];

return;}

void

find(ll rt,ll l,ll r,ll l,ll r)

else

if(l<=(l+r)/2

)

if(r>(l+r)/2

)

su[rt]=su[2*rt]+su[2*rt+1

]; }

return;}

intmain()

if(a==2)//

查詢 }

for(int i=1;i<=c;i++)

}

這樣,一棵完完整整的基礎簡化版

的線段樹就寫完了

如果你看完上面的覺得很簡單,那你可以繼續學習接下來的zkw線段樹了

但是如果你覺得沒那麼簡單,一定去練幾個題再回來看下面的

zkw線段樹不知道比遞迴線段樹快到**去了,跑得嗷嗷的

看不懂就畫圖,手模

n是陣列大小

單點修改 區間求和

#include#include

using

namespace

std;

intn,m,q,ans,l,r;

int t[300000

];char q1[3

];void build()//

非遞迴建樹,從m+1開始,多餘的空間我不要了(任性)

void change(int x,int a)//

這個不能再短

void ask(int l,int

r) printf(

"%d\n

",ans);

}int main()//

按需填寫

區間修改 區間求和

#include#include

using

namespace

std;

void build()//

建樹一樣的

void change(int l,int r,int k)//

標記不下傳,永久化

if(r&1)//

右指標是右兒子,同理

}for(;l;l>>=1,r>>=1)//

加到底}

void ask(int l,int

r)

if(r&1

)

}for(;l;l>>=1,r>>=1)//

加到底}

intmain()

區間修改 區間最值

#include#include

using

namespace

std;

intl,r,n,q,m,ans,k,a;

int t[100000

];void

build()

}void change(int l,int r,int

k)

for(;l!=1;l>>=1)//

差分到底

}void ask(int l,int

r) }

ans=min(lans+t[l],rans+t[r]);

while(s>1) ans+=t[s>>=1];//

一定要到底

printf("

%d\n

",ans);

}int

main()

有問題的話可以問呦~雖然我也不一定會但是我會盡力解答的!

感謝閱讀,求讚

zkw線段樹小結

zkw zkwzk w線段樹作為迴圈式線段樹具有較小的常數.其實樹狀陣列本質上就是線段樹 下標為 1,n 1,n 1,n 預處理乙個2 k n2 k n 2k n.然後總空間為2k 12 2k 1 2 k 1 4n 2 4n 2k 1 4n 後面令k 2 kk 2 k k 2k 那麼乙個葉子x xx...

鏈結 zkw線段樹

資料結構 走近zkw線段樹 一 資料結構 走近zkw線段樹 二 線段樹的擴充套件之 zkw線段樹 include define lc x x 1 define rc x x 1 1 using namespace std const int maxn 100005 int max int a,int...

ZKW線段樹 非遞迴版本的線段樹

學習和參考 下面是支援區間修改和區間查詢的zkw線段樹模板,先記下來。include include include include include include include include include include include include include include inc...