介绍
线段树的应用场景在区间修改和区间查询上,修改和查询的时间复杂度都为O(log n)
线段树如它的名字一样将区间分成一段一段的,如123456789这一串数字,以区间求和为例
上图就是构建的线段树,其中结点里的值是该结点表示的区间的和,中括号里的数字代表表示的区间
实现(以区间求和为例)
定义
线段树的根结点定义为1,那么左子节点就是n*2,右子节点是n*+1
通常2*n在代码中写成 n<<1 。 2*n+1写成 n<<1|1 。
线段树所需的空间
足够的空间 = 数组大小n的四倍。
实际上足够的空间 = (n向上扩充到最近的2的某个次方)的两倍。
1 | const int maxn = 100005;//元素的个数 |
建树
1 | void push_up(int id)//根据子节点来更新父节点 |
1 | void build(int l, int r, int id)//l,r表示当前区间,id表示当前结点的下标 |
单点修改
1 | void update(int l, int r, int now, int number, int pos) |
区间修改
关于区间的修改需要用到标记数组,改数组的含义是”当前结点的值已经更新,但是子节点的值未更新”
如add[7]=3,代表结点7的值已经更新,但是结点7的子节点都还没有加上这个值,这就需要一个下推标记的函数
1 | void push_down(int id, int ln, int rn) |
修改
1 | void update(int l, int r, int L, int R, int id, int number) |
查询
1 | int query(int l, int r, int L, int R, int id)//L,R是需要查询的区间 |
例题
HDU1166
单点修改的例子
1 |
|
POJ 3468
区间修改的例子
1 |
|