线段树模板,两种实现方式(结构体一维数组模拟满二叉树、结构体+链式存储)

发布时间 2023-03-27 22:04:24作者: 深情的山鸡

简单总结下线段树值得注意的点,对于什么是线段树,网上有非常多大佬写的非常的详细,我这里只是给大家提供两个不同存储结构实现的线段树模板

线段树

  • 主要是实现区间操作,区间查询,有懒标记的线段树能够实现区间更新(包含单点更新),没有懒标记的则只有单点更新(其实也可以区间更新只不过这样是O(n)的时间没啥意义)
  • 线段树类型:区间最值线段树、区间求和线段树、gcd线段树...(类型非常多,其实我也没见过几种)
  • 数据结构:结构体+链式存储(更快)、结构体一维数组模拟满二叉树

  • 树上的每个节点都代表一个区间

  • 查询方式:覆盖查询(还有一种是等区间查询相对而言用的少一点)

  • pushdown()下传懒标记,一次只下传一层

  • 数组模拟需要开4n的空间
  • 线段树的五个基本操作pushup()、build()、pushdown()、update()、query()
  • 对于数组模拟的满二叉树,如果树的起始存储点从0开始,那么它的左右孩子位置为2*i+1、2*i+2;如果树的起始存储点从1开始,那么它左右孩子位置为2*i、2*i+1(本例中用的就是这种)
  • 待续...

 

 

此模板依据的是洛谷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));
            }
        }
    }
}