前言
线段树是一种常用的数据结构,它主要用于维护区间信息(要求满足结合律)。与树状数组相比,它可以实现 O(log n) 的区间修改,还可以同时支持多种操作(加、乘等),更具通用性。
一、线段树的基本概念
二、线段树的一些模板
1.建树
void build (ll l ,ll r,ll p)
{
if (l == r) tree[p].sum = a[l];
else
{
ll mid = (l + r) >> 1;
build(l, mid, p << 1)
build(mid + 1 , r, p << 1|1);
}
tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
}
2.区间求和
//[s,t]指代求区间,p指当前节点
//tree[p].l指当前节点区间的左端点,同理tree[p].r指当前区间的右端点
ll query(ll s ,ll t,ll p)
{
if (s <= tree[p].l && t >= tree[p].r) //代求区间包括此段
return tree[p].sum;
//代求区间与此段有交集或者无交集
ll mid = (tree[p].l + tree[p].r) >> 1;
ll ans = 0;
//代求区间在该节点左孩子的区间范围
if (s <= mid)
{
ans += query(s, t, p << 1);
}
//代求区间在该节点右孩子的区间范围
if (t > mid)
{
ans += query(s, t, p << 1 | 1);
}
return ans;
}
3.区间修改
以把区间里的数变为v为例:
//可以这样写
//一般加法和乘法等要用到pushdown,在这篇文章就不赘述了
void update(ll s ,ll t, ll p, ll v)
{
if (tree[p].l == tree[p].r)
{
tree[p].sum = v;
return;
}
ll mid = (tree[p].l + tree[p].r) >> 1;
if (s <= mid)
update(s, t, p << 1, v);
if (l > mid)
update(s, t, p << 1 | 1, v);
tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
}