其实只要看section_query(区间查询)和node_modify(单点修改)这两个函数就好.我习惯用了结构体.
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <memory.h>
#define endl '\n'
#define ref(i, a, b, p) for (signed(i) = (a); (i) <= signed(b); (i) += signed(p))
#define gef(i, a, b, p) for (signed(i) = (a); (i) >= signed(b); (i) -= signed(p))
#define LL long long
using namespace std;
const int maxn = 5e5 + 5;
int n, m;
int a[maxn];
struct common_segment_tree // 普通线段树
{
struct tree // 节点
{
int l, r, num, lz; // 左端点, 右端点, 值
// tree() {} // 空构造函数
// tree(const int l, const int r, const int num) // 赋值构造函数
// {
// this->l = l, this->r = r, this->num = num;
// }
};
tree tr[maxn * 40];
void build(int i, int l, int r) // 建立线段树, i 为当前节点, l 为区间左端点, r 为区间右端点
{
tr[i].l = l, tr[i].r = r; // 初始化
if (l == r) // 如果是叶子节点, 那么赋值然后返回
{
tr[i].num = a[l];
return;
}
int mid = (l + r) >> 1; // 不是叶子结点, 那么接着分叉
build(i << 1, l, mid); // 左子树
build(i << 1 | 1, mid + 1, r); // 右子树
return;
}
void push_down(int i) // 向下传递 "懒" 标记, i 为当前节点
{
if (tr[i].lz != 0)
{
tr[i << 1].lz += tr[i].lz;
tr[i << 1 | 1].lz += tr[i].lz;
int mid = (tr[i].l + tr[i].r) >> 1;
tr[i << 1].num += tr[i].lz * (mid - tr[i << 1].l + 1);
tr[i << 1 | 1].num += tr[i].lz * (tr[i << 1 | 1].r - mid);
tr[i].lz = 0;
}
return;
}
void node_modify(int i, int x, int k) // 单点修改, i 为当前节点, x 为待修改节点下标, k 为待修改值
{
if (tr[i].l == tr[i].r) // 叶子结点说明已经找到, 加上值直接返回
{
tr[i].num += k;
return;
}
if (x <= tr[i].r) // 在左子树
node_modify(i << 1, x, k);
else // 在右子树
node_modify(i << 1 | 1, x, k);
tr[i].num = tr[i << 1].num + tr[i << 1 | 1].num; // 维护当前节点的值
return;
}
int section_query(int i, int l, int r) // 区间查询, i 为当前节点, l 为区间左端点, r 为区间右端点
{
if (tr[i].l >= l && tr[i].r <= r) // 表示这个区间被所查询区间包含, 则直接返回这个区间的值
return tr[i].num;
if (tr[i].r < l || tr[i].l > r) // 表示这个区间与所查询区间的交集为空集, 则直接返回 0
return 0;
push_down(i); // 向下传递 "懒" 标记
int ans = 0, mid = (l + r) >> 1;
if (mid >= l) // 左子树与所查询区间有交集, 递归查询
ans += section_query(i << 1, l, r);
if (tr[i << 1 | 1].l <= r) // 右子树与所查询区间有交集, 递归查询
ans += section_query(i << 1 | 1, l, r);
return ans;
}
void section_modify(int i, int k, int l, int r) // 区间修改, i为当前节点, k 为需要修改的值, l 为区间左端点, r 为区间右端点
{
if (tr[i].l >= l && tr[i].r <= r) // 表示这个区间在要修改的范围内,那么修改完返回
{
tr[i].num += k * (tr[i].r - tr[i].l + 1);
tr[i].lz += k;
return;
}
push_down(i); // 如果不在区间内
if (tr[i << 1].r >= l) // 左区间与要修改范围有交集
section_modify(i << 1, k, l, r); // 在此区间的左子树内查找
if (tr[i << 1 | 1].l <= r) // 有区间与要修改范围有交集
section_modify(i << 1 | 1, k, l, r); // 在此区间的右子树内查找
tr[i].num = tr[i << 1].num + tr[i << 1 | 1].num; // 维护当前节点的值
return;
}
int node_query(int i, int k, int ans) // 单点查询, i 为当前节点, k 为要查找的数的下标, ans 为值
{
ans += tr[i].num; // 找到一个点就相加
if (tr[i].l == tr[i].r) // 如果是叶子节点就返回
return ans;
int mid = (tr[i].l + tr[i].r) >> 1; // 不是叶子节点就继续分叉
if (k <= mid) // 值在左子树
return node_query(i << 1, k, ans);
else // 值在右子树
return node_query(i << 1 | 1, k, ans);
}
};
// struct
common_segment_tree cst;
void simulation_common_segment_tree() // 调试普通线段树, 洛谷 P3368 P3374, 已 AC. 普通线段树板子题
{
cin >> n >> m; // n 为集合大小, m 为操作次数
ref(i, 1, n, 1) // 输入
cin >>
a[i];
cst.build(1, 1, n); // 建立线段树
ref(i, 1, m, 1)
{
int opr = 0;
cin >> opr;
// if (opr == 1) // opr = 1 为区间修改
// {
// int x = 0, y = 0, k = 0;
// cin >> x >> y >> k; // k 为修改值, x 为区间左端点, y 为区间右端点
// cst.section_modify(1, k, x, y);
// }
// else if (opr == 2) // opr = 2 为单点查询
// {
// int x = 0;
// cin >> x; // x 为查询数的下标
// cout << cst.node_query(1, x, 0) << endl;
// }
if (opr == 1) // opr = 3 为单点修改
{
int x = 0, k = 0;
cin >> x >> k; // x 为查询数的下标, k 为待修改的值
cst.node_modify(1, x, k);
}
else if (opr == 2) // opr = 4 为区间查询
{
int x = 0, y = 0;
cin >> x >> y; // x 为欲查询区间左端点, y 为欲查询区间右端点
cout << cst.section_query(1, x, y) << endl;
}
}
return;
}
void work()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
simulation_common_segment_tree();
return;
}
int main()
{
work();
return 0;
}