原出处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); }