原出處https://tjor.blog.luogu.org/xian-duan-shu-yu-shu-zhuang-shuo-zuui
線段樹是一種二叉搜索樹,與區間樹類似,它將一個區間劃分紅一些單元區間,每一個單元區間對應線段樹中的一個葉結點。code
使用線段樹能夠快速的查找某一個節點在若干條線段中出現的次數,時間複雜度爲O(logN)。blog
好比講一個有4個數的線段樹,是長這個樣子的:
遞歸
一號節點,表明着區間1~4class
二號節點,表明區間1~2搜索
三號節點,表明區間3~4im
以此類推。。。。。。查詢
很容易發現,對於n號節點來講,n×2表明着它的區間的前半段,n×2+1表明着它的區間的後半段。img
就是用到遞歸:先設left=1,right=n,而後每一次遞歸,left、mid和mid+一、right。代碼以下:di
void build(int left,int right,int index) { 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 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); }
單點修改就是每到一個節點,看這個節點表明着的區間包括不包括這個點,包括就加上。
void my_plus(int index,int dis,int k) { tree[index].num+=k; if(tree[index].left==tree[index].right) return ; if(dis<=tree[index*2].right) my_plus(index*2,dis,k); if(dis>=tree[index*2+1].left) my_plus(index*2+1,dis,k); }
區間查詢就是,每查到一個區間,有三種選擇: 一、若是這個區間被徹底包括在目標區間內,那麼加上這個區間的和,而後return; 二、若是這個區間的right>目標區間的left,那麼查詢這個區間; 三、若是這個區間的left<目標區間的right,也查詢這個區間;
void search(int index,int l,int r) { if(tree[index].left>=l && tree[index].right<=r) { ans+=tree[index].num; return ; } if(tree[index*2].right>=l) search(index*2,l,r); if(tree[index*2+1].left<=r) search(index*2+1,l,r); }
和線段樹區間查詢相似,分爲三種 一、若是當前區間徹底屬於要加的區間,那麼這個區間,也就是節點加上,而後return; 二、若是這個區間的right>目標區間的left,那麼查詢這個區間; 三、若是這個區間的left<目標區間的right,也查詢這個區間;
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); }