线段树模板,两种实现方式(结构体一维数组模拟满二叉树、结构体+链式存储)
简单总结下线段树值得注意的点,对于什么是线段树,网上有非常多大佬写的非常的详细,我这里只是给大家提供两个不同存储结构实现的线段树模板
线段树
- 主要是实现区间操作,区间查询,有懒标记的线段树能够实现区间更新(包含单点更新),没有懒标记的则只有单点更新(其实也可以区间更新只不过这样是O(n)的时间没啥意义)
- 线段树类型:区间最值线段树、区间求和线段树、gcd线段树...(类型非常多,其实我也没见过几种)
-
数据结构:结构体+链式存储(更快)、结构体一维数组模拟满二叉树
-
树上的每个节点都代表一个区间
-
查询方式:覆盖查询(还有一种是等区间查询相对而言用的少一点)
-
pushdown()下传懒标记,一次只下传一层
- 数组模拟需要开4n的空间
- 线段树的五个基本操作pushup()、build()、pushdown()、update()、query()
- 对于数组模拟的满二叉树,如果树的起始存储点从0开始,那么它的左右孩子位置为2*i+1、2*i+2;如果树的起始存储点从1开始,那么它左右孩子位置为2*i、2*i+1(本例中用的就是这种)
- pushup()中的root->right 和 root->left不用判空,因为在使用pushup()操作之前就已经确定了调用该操作的结点不是叶子结点
- update()和query()的操作区间[l,r]都是整个区间递归往下传,这样是做可以对所有的当前结点所对应区间进行覆盖
- 待续...
此模板依据的是洛谷P3372 【模板】线段树 1
结构体+链式存储模板的运行时间
结构体+一维数组模拟满二叉树的运行时间
从两个模板的运行时间可以看出结构体+链式存储的运行时间更快
(对于该题此模板可直接提交)
1.结构体+链式存储
namespace n8 { #define ll long long const int N = 1e6 + 5; int a[N]; struct Tree { ll l, r, val, add; Tree *left, *right; Tree(){ l = r = val = add = 0; left = right = NULL; } }; void pushup(Tree *root) { root->val = root->left->val + root->right->val; } void build(Tree *&root, ll l, ll r) { root = new Tree(); root->l = l, root->r = r,root->add = 0; if (l == r) { root->val = a[l]; return; } ll mid = (l + r) / 2; build(root->left, l, mid); build(root->right, mid + 1, r); pushup(root); } void pushdown(Tree *root) { if (root->add) { root->left->add += root->add; root->left->val += (ll)(root->left->r - root->left->l + 1) * root->add; root->right->add += root->add; root->right->val += (ll)(root->right->r - root->right->l + 1) * root->add; root->add = 0; } } void update(Tree *root, ll l, ll r, ll v) { if (root->l >= l && root->r <= r) { root->add += v, root->val += (ll)(root->r - root->l + 1) * v; return; } pushdown(root); ll mid = (root->l + root->r) / 2; if (l <= mid) update(root->left, l, r, v); if (r > mid) update(root->right, l, r, v); pushup(root); } ll query(Tree *root, ll l, ll r) { if (root->l >= l && root->r <= r) return root->val; pushdown(root); ll res = 0; ll mid = (root->l + root->r) / 2; if (l <= mid) res += query(root->left, l, r); if (r > mid) res += query(root->right, l, r); return res; } void test() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); Tree *root; build(root, 1, n); while (m--) { int t; scanf("%d", &t); if (t == 1) { int x, y, k; scanf("%d%d%d", &x, &y, &k); update(root, x, y, k); } else { int x, y; scanf("%d%d", &x, &y); printf("%ld\n", query(root, x, y)); } } } }
2.结构体+一维数组模拟满二叉树
namespace n9 { #define ll long long const ll N = 1e6 + 5; ll a[N]; struct Tree { ll val, l, r, add; } tr[4 * N]; void pushup(int u) { tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val; } void build(ll u, ll l, ll r) { tr[u].l = l; tr[u].r = r; tr[u].add = 0; if (l == r) { tr[u].val = a[l]; return; } ll mid = (l + r) / 2; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); pushup(u); } void pushdown(ll u) { if (tr[u].add) { tr[u << 1].add += tr[u].add; tr[u << 1 | 1].add += tr[u].add; tr[u << 1].val += (tr[u << 1].r - tr[u << 1].l + 1) * tr[u].add; tr[u << 1 | 1].val += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * tr[u].add; tr[u].add = 0; } } void update(ll u, ll l, ll r, ll v) { if (tr[u].l >= l && tr[u].r <= r) { tr[u].val += (tr[u].r - tr[u].l + 1) * v; tr[u].add += v; return; } pushdown(u); ll mid = (tr[u].l + tr[u].r) / 2; if (l <= mid) update(u << 1, l, r, v); if (r > mid) update(u << 1 | 1, l, r, v); pushup(u); } ll query(ll u, ll l, ll r) { if (tr[u].l >= l && tr[u].r <= r) return tr[u].val; pushdown(u); ll res = 0; ll mid = (tr[u].l + tr[u].r) / 2; if (l <= mid) res += query(u << 1, l, r); if (r > mid) res += query(u << 1 | 1, l, r); return res; } void test() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); build(1, 1, n); while (m--) { int t; scanf("%d", &t); if (t == 1) { int x, y, k; scanf("%d%d%d", &x, &y, &k); update(1, x, y, k); } else { int x, y; scanf("%d%d", &x, &y); printf("%lld\n", query(1, x, y)); } } } }