线段树
一 线段树简介
线段树是一种数据结构,对于RMQ,RSQ问题可以达到O(log(n))的区间修改,同时还支持多种加乘的操作,是一种常见的数据结构
二 线段树的数据结构
在这里我们可以设计一个结构体去存储一个节点的信息,有左边界,右边界,懒惰标记,区间内数之和等附加数据功能
对于单点的数据,他的左边界等于右边界
懒惰标记是我们实现O(logn)区间修改的关键点。即如果我们要修改一个区间内的数,那么我们只要修改管理这个大区间的点即可,没有必要对它管理的分支进行进一步操作,等到下一步需要操作时在将这个懒惰标记向下传导。
三 线段树的代码实现
1数据结构
struct SergmentNode
{
int l;
int r;
int lazy;
int sum;
}elem[N];
2 建树
void bulid(int p,int r,int l)
{
elem[p].l = l;
elem[p].r = r;
if(l==r) elem[p].sum = number[r];
else
{
int mid = (l+r)>>1;
bulid(ls(p), l, mid);
bulid(rs(p), mid+1, l);
elem[p].sum = elem[rs(p)].sum + elem[ls(p)].sum;
}
}
3 传导懒惰标记
void spread(int p)
{
if(elem[p].lazy)
{
elem[ls(p)].sum += (elem[ls(p)].r - elem[ls(p)].l + 1)*elem[p].lazy;
elem[rs(p)].sum += (elem[rs(p)].r - elem[rs(p)].l + 1)*elem[p].lazy;
elem[ls(p)].lazy += elem[p].lazy; // 增加标记
elem[rs(p)].lazy += elem[p].lazy;
elem[p].lazy = 0;// 传导后直接清空慵懒标记
}
}
4 区间修改
void update(int p,int l,int r,int add)
{
if(l<=elem[p].l&&r>=elem[p].r)
{
elem[p].sum += (elem[p].r - elem[p].l + 1)*add;
elem[p].lazy += add;
return;
}
// 如果这个区间被包含在要修改的区间内
int mid = (elem[p].r + elem[p].l)>>1;
if(l <= mid) update(ls(p), l, r, add);
if(r >= mid) update(rs(p), l, r, add);
elem[p].sum += elem[ls(p)].sum + elem[rs(p)].sum;
// 增加和数
}
5 区间查询
int ask(int p,int l,int r)
{
if(l<=elem[p].l&&r>=elem[p].r) return elem[p].sum;
spread(p);
int mid = (elem[p].r+elem[p].l)>>1;
int ans = 0;
if(l <= mid) ans+=ask(ls(p), l, r);
if(r >= mid) ans+=ask(rs(p), l, r);
return ans;
}
写在最后,考试周,写的匆忙,勿怪