专题1.5 - 写个线段树板子

专题1.5 - 写个线段树板子

Update

2022.2.13 更新代码,\(ql,qr\)不随着更新与查询改变。

线段树什么功能就不需要多说了,这边主要是讲讲\(lazy\)标记的作用。

普通的线段树,修改的效率为\(\Omicron (n\log n)\),查询效率为\(\Omicron (n\log n)\),修改的效率甚至比直接修改还慢(当然直接修改就没法维护线段树了),那么怎么可以提高修改的效率?

这里需要引入\(lazy\)标记的概念,具体原理如下:

1.修改时,如果修改范围恰好为当前区间范围,停止向下搜索,打上\(lazy\)标记。

2.修改时,如果范围并非恰好,需要向下传递一次\(lazy\)标记。

3.查询时,优先向下传递\(lazy\)标记。

这边给出两个模板:

洛谷P3372

#include <bits/stdc++.h>
#define ll long long
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
const int maxn = 100010;
ll a[4 * maxn];
ll lazy[4 * maxn];
ll temp[maxn];
void build(int o, int l, int r)
{
    if (l == r)
        a[o] = temp[l];
    else
    {
        int m = (l + r) / 2;
        int ls = 2 * o, rs = 2 * o + 1;
        build(ls, l, m);
        build(rs, m + 1, r);
        a[o] = a[ls] + a[rs];
    }
}
void pushdown(int o, int l, int r)
{
    int m = (l + r) / 2;
    int ls = 2 * o, rs = 2 * o + 1;
    lazy[ls] += lazy[o];
    lazy[rs] += lazy[o];
    a[ls] += lazy[o] * (m - l + 1);
    a[rs] += lazy[o] * (r - m);
    lazy[o] = 0;
}
void Update(int o, int l, int r, int ql, int qr, int d)
{
    if (l >= ql && r <= qr)
    {
        a[o] += (r - l + 1) * d;
        lazy[o] += d;
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) / 2;
    int ls = 2 * o, rs = 2 * o + 1;
    if (mid >= ql)
        Update(ls, l, mid, ql, qr, d);
    if (mid < qr)
        Update(rs, mid + 1, r, ql, qr, d);
    a[o] = a[ls] + a[rs];
}
ll query(int o, int l, int r, int ql, int qr)
{
    if (l >= ql && r <= qr)
        return a[o];
    pushdown(o, l, r);
    int mid = (l + r) / 2;
    int ls = 2 * o, rs = 2 * o + 1;
    ll ans = 0;
    if (mid >= ql)
        ans += query(ls, l, mid, ql, qr);
    if (mid < qr)
        ans += query(rs, mid + 1, r, ql, qr);
    return ans;
}
int main()
{
    fast;
    int n, q;
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> temp[i];
    }
    memset(lazy, 0, sizeof(lazy));
    build(1, 1, n);
    while (q--)
    {
        int opt, l, r, k;
        cin >> opt;
        if (opt == 1)
        {
            cin >> l >> r >> k;
            Update(1, 1, n, l, r, k);
        }
        else
        {
            cin >> l >> r;
            cout << query(1, 1, n, l, r) << '\n';
        }
    }
}

洛谷P3373

#include <bits/stdc++.h>
#define ll long long
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
const int maxn = 100010;
ll a[4 * maxn];
ll add[4 * maxn];
ll mul[4 * maxn];
ll temp[maxn];
int n, m;
ll p;
void build(int o, int l, int r)
{
    if (l == r)
        a[o] = temp[l];
    else
    {
        int m = (l + r) / 2;
        int ls = 2 * o, rs = 2 * o + 1;
        build(ls, l, m);
        build(rs, m + 1, r);
        a[o] = a[ls] + a[rs];
        a[o] = a[o] % p;
    }
}
void pushdown(int o, int l, int r)
{
    int m = (l + r) / 2;
    int ls = 2 * o, rs = 2 * o + 1;
    a[ls] = (a[ls] * mul[o] + add[o] * (m - l + 1)) % p;
    a[rs] = (a[rs] * mul[o] + add[o] * (r - m)) % p;
    mul[ls] = mul[ls] * mul[o] % p;
    mul[rs] = mul[rs] * mul[o] % p;
    add[ls] = (add[ls] * mul[o] + add[o]) % p;
    add[rs] = (add[rs] * mul[o] + add[o]) % p;
    mul[o] = 1;
    add[o] = 0;
}
void Update_add(int o, int l, int r, int ql, int qr, ll d)
{
    if (l >= ql && r <= qr)
    {
        a[o] = (a[o] + (r - l + 1) * d) % p;
        add[o] += d;
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) / 2;
    int ls = 2 * o, rs = 2 * o + 1;
    if (mid >= ql)
        Update_add(ls, l, mid, ql, qr, d);
    if (mid < qr)
        Update_add(rs, mid + 1, r, ql, qr, d);
    a[o] = (a[ls] + a[rs]) % p;
}
void Update_mul(int o, int l, int r, int ql, int qr, ll d)
{
    if (l >= ql && r <= qr)
    {
        a[o] = (a[o] * d) % p;
        mul[o] = mul[o] * d % p;
        add[o] = add[o] * d % p;
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) / 2;
    int ls = 2 * o, rs = 2 * o + 1;
    if (mid >= ql)
        Update_mul(ls, l, mid, ql, qr, d);
    if (mid < qr)
        Update_mul(rs, mid + 1, r, ql, qr, d);
    a[o] = (a[ls] + a[rs]) % p;
}
ll query(int o, int l, int r, int ql, int qr)
{
    if (l >= ql && r <= qr)
        return a[o] % p;
    pushdown(o, l, r);
    int mid = (l + r) / 2;
    int ls = 2 * o, rs = 2 * o + 1;
    ll ans = 0;
    if (mid >= ql)
        ans += query(ls, l, mid, ql, qr), ans %= p;
    if (mid < qr)
        ans += query(rs, mid + 1, r, ql, qr), ans %= p;
    return ans % p;
}
int main()
{
    fast;
    cin >> n >> m >> p;
    for (int i = 1; i <= n; i++)
    {
        cin >> temp[i];
    }
    for (int i = 1; i <= 4 * n; i++)
    {
        add[i] = 0;
        mul[i] = 1;
    }
    build(1, 1, n);
    while (m--)
    {
        int opt, x, y, k;
        cin >> opt >> x >> y;
        if (opt == 2)
        {
            cin >> k;
            Update_add(1, 1, n, x, y, k);
        }
        if (opt == 1)
        {
            cin >> k;
            Update_mul(1, 1, n, x, y, k);
        }
        if (opt == 3)
        {
            cout << query(1, 1, n, x, y) << '\n';
        }
    }
}

值得注意的是,需要注意向下传递(lazy)的顺序,以及对结果的影响。

posted @ 2021-09-24 11:17  cyanine_告别  阅读(45)  评论(0编辑  收藏  举报