关于线段树我之前就写过博客也转载过他人的博客来介绍线段树。
递归版线段树:https://blog.csdn.net/hpu2022/article/details/81946151
非递归版线段树:https://blog.csdn.net/hpu2022/article/details/81946200
我自己以前写过的线段树模板:https://blog.csdn.net/hpu2022/article/details/81913776
YouTube上黄浩杰关于线段树的视频(感觉还不错):
https://www.youtube.com/watch?v=e_bK-dgPvfM
这次又重学了一遍线段树,下面是重新总结的模板、
主要内容是:建树,单点更新,区间更新,区间查询,lazy标记
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
#define ls node*2 + 1 // 左儿子
#define rs node*2 + 2 // 右儿子
int tree[N*4], lazy[N*4]; // 线段树和懒标记
int a[N], n; // 数组元素和个数
// 这里只是把建树递归后的操作提取出来做成一个函数
// node表示当前结点的编号
void push_up(int node)
{
tree[node] = tree[ls] + tree[rs];
}
// 建树 start end是原始数组的区间范围
void build(int start, int end, int node)
{
if(start == end)
{
tree[node] = a[start];
return;
}
int mid = (start + end) / 2;
build(start, mid, ls);
build(mid+1, end, rs);
push_up(node); // 左右子树建好以后就可以求当前节点的值了
}
// 单点更新 a[idx] = val;
// 带node是线段树的下标,node是当前线段树的当前所在节点的编号
void update(int idx, int val, int start, int end, int node)
{
if(start == end)
{
tree[node] = val; // 修改树中的节点
a[idx] = val; // 修改原始数组
return;
}
int mid = (start + end) / 2;
// 如果需要修改的点再左分支
if(start <= idx && idx <= mid)
update(idx, val, start, mid, ls);
else
update(idx, val, mid+1, end, rs);
push_up(node);
}
// node表示当前节点,ln,rn分别表示左右子树的节点数量
void push_down(int node, int ln, int rn)
{
if(lazy[node])
{
lazy[ls] += lazy[node]; // 下推标记
lazy[rs] += lazy[node];
tree[ls] += lazy[node] * ln; // 修改子节点的Sum使之与对应的lazy相对应
tree[rs] += lazy[node] * rn;
lazy[node] = 0; // 清除本结点标记
}
}
// 区间更新
// L R表示操作区间,start, end表示当前节点区间,node是当前节点编号
void update2(int L, int R, int val, int start, int end, int node)
{
if(L <= start && end <= R)
{
tree[node] += val * (end - start + 1);// 更新数字和,向上保持正确
lazy[node] += val;// 增加lazy标记,表示本区间的Sum正确,子区间的Sum仍需要根据lazy的值来调整
return;
}
int mid = (start + end) / 2;
push_down(node, mid - start + 1, end - mid);
if(L <= mid) // 这里判断左右子树跟[L,R]有无交集,有交集才递归
update2(L, R, val, start, mid, ls);
if(R > mid)
update2(L, R, val, mid+1, end, rs);
push_up(node);
}
// 区间查询
// L, R表示查询区间,start, end表示当前节点区间,node表示当前节点编号
int query(int L, int R, int start, int end, int node)
{
if(R < start || L > end) // 没有交集
return 0;
else if(start == end) // 区间缩小到1时
return tree[node];
else if(start <= L && R >= end) // 完全在区间内
return tree[node];
int mid = (start + end) / 2;
push_down(node, mid - start + 1, end - mid);
int ans = 0;
ans += query(L, R, start, mid, ls);
ans += query(L, R, mid+1, end, rs);
return ans;
}
int main()
{
while(scanf("%d", &n) != EOF)
{
for(int i=0; i<n; i++)
{
scanf("%d", &a[i]);
}
build(0, n-1, 0);
// update(4, 6, 0, n-1, 0);
printf("\n");
for(int i=0; i<n*2+2; i++)
{
printf("tree[%d] = %d\n", i, tree[i]);
}
printf("\n");
update2(0, 2, 3, 0, n-1, 0);
for(int i=0; i<n*2+2; i++)
{
printf("tree[%d] = %d\n", i, tree[i]);
}
int ans = query(0, 1, 0, n-1, 0);
printf("%d\n", ans);
for(int i=0; i<n*2+2; i++)
{
printf("tree[%d] = %d\n", i, tree[i]);
}
}
return 0;
}