好啦,我們就開始說說線段樹吧
線段樹是個支援區間操作和查詢的東東,平時的話還是蠻實用的
下面以最基本的區間加以及查詢區間和為例
線段樹顧名思義就是棵樹嘛,葉子節點是每個基本點,它們所對應的父親就是它們的和,具體如下圖
但是對於這樣的線段樹來說,操作所需的時間是遠達不到我們的要求的(會被t),因為我們會進行一些不必要的操作,就像如果沒有查詢到某個點,那麼就沒有必要去修改這個點的值,為此,我們會引入乙個懶標記,記錄每個基本點需要被加上的值(稱為add),那麼樹上任意乙個點需要增加的值=該點對應的區間長度*add
那麼總的來說,線段樹的基本操作我個人認為可以分成3個,建樹、修改和查詢,當然如果繼續細分也是口以(可以)的,就比如說還可以分出 區間和的向上傳遞(父親節點等於子節點的和)和懶標記的向下傳遞(子節點的懶標記=原來的懶標記+父節點的懶標記)
所以接下來我們就來看看建樹、修改和查詢這3部分的具體**吧(深呼吸)
首先是建樹(build)
#define ls 2*rt,l,(l+r)/2 //然後是修改(change)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;}
//一層一層的求,我們就可以建好乙個初步的樹啦
#define ls 2*rt,l,(l+r)/2 //呼啊,已經完成2/3了,堅持就是勝利!↖(^ω^)↗左右兒子,和之前一樣
#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
;}
查詢(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...