1.线段树的作用
给定一个整数序列,让你完成如下操作
修改序列上某个位置(区间)上的数
询问序列中某个区间的和
“暴力”算法
单点修改$O(1)$
询问区间和$O(区间长度)$
“前缀和”算法
单点修改$O(区间长度)$
询问区间和$O(1)$
线段树
$O(nlogn)$
2.线段树的概念
线段树是一棵二叉树,树上的每个结点对应序列的一段区间
3.线段树的操作
1.结构体
1 | struct node |
2.建树
基本思想:
1 二分
2 对于二分到的每一个结点,把左右端点的信息储存
3 叶结点输入
1
2
3
4
5
6
7
8
9
10
11
12
13void build(int l,int r,int k)
{
tree[k].l =l,tree[k].r =r;
if(tree[k].l==tree[k].r)//叶子结点
{
scanf("%d",&tree[k].w);
return;
}
int m=(l+r)/2;
build(l,m,k*2);//左孩子
build(m+1,r,k*2+1);//右孩子
tree[k].w =tree[k*2].w +tree[k*2+1].w ;//合并
}
3.单点查询
1 | void ask(int k) |
4.单点修改
1 | void add(int k) |
5.区间求和
1 | void sum(int k) |
6.区间修改
在前面的操作中我们经常看到有懒标记下传这个操作,那么懒标记到底是什么呢
1.1懒标记的作用
存储到这个节点的更新信息,暂时不把更新信息传到子节点。
1.2懒标记的下传
1 将当前节点的懒标记累加到子节点的懒标记中
2 修改子节点状态,即区间和$w$=原状态+区间长度×父节点下传的懒标记
3 父节点懒标记清空
1
2
3
4
5
6
7
8void down(int k)
{
tree[k*2].f +=tree[k].f ;//修改子节点懒标记(下同)
tree[k*2+1].f +=tree[k].f ;
tree[k*2].w +=tree[k].f *(tree[k*2].r -tree[k*2].l +1);//修改子节点状态(下同)
tree[k*2+1].w +=tree[k].f *(tree[k*2+1].r -tree[k*2+1].l +1);
tree[k].f =0;//懒标记清零
}
2.区间修改
1 | void add2(int k)//y为增加的数,(a,b)为目标区间 |
4.完整代码
1 |
|
5.推荐例题
思路点拨:线段树模板
思路点拨:线段树模板