JOURNAL ENTRY

线段树 II 之 懒惰标记

线段树 II 之 懒惰标记

前置知识:线段树 I – ztr 的小窝 (ztrztr.top)

懒惰标志 Lazy Tag

懒惰标志,是维持线段树的区间修改查询的复杂度在 $O(\log n)$ 级别的一个方法。

正常的区间修改查询的复杂度可以说是 $O(n)$ 级别的,因为每次修改一个,需要一直传到叶子节点为止。

但是我们发现,如果这样的话,修改的复杂度会很高,堪比暴力。我们需要想一个办法来解决。

那么这里我们可以引入 Lazy Tag。

比如一个数组 $1, 2, 3, 4, 5, 6$,我们要对区间 $[2,5]$ 加一,如果暴力的话是直接遍历把,每一位加一。

如果不用 Lazy Tag,那么线段树也要递归到叶子节点去修改,常数甚至比暴力还高。

但是,我们发现,如果递归找到一个合适的区间,比如说我们递归到了 $[4,5]$,这时我们完全没必要继续往下遍历,只用修改这个区间,然后回溯的时候就可以自动修改上层的区间。

那我们还要不要往下呢?这时候我们可以让 $lazy[i]$ 的值增加修改量,然后再修改儿子节点,就行了,没必要继续往下。

查询的时候,我们也再递归前先 push\_down 一下,把现在的这个线段的儿子更新一下。

注意一下 push\_down 的最后要清空 laze[i],因为已经用不上了。

如果操作有好几种,需要注意先乘再加,必然会有一些问题。

小技巧

有些题目,比如说 GSS4 - Can you answer these queries IV - 洛谷 | 计算机科学教育新生态 (luogu.com.cn),如果想要用 Lazy Tag,有点难做,至少我没有想出来。

这个时候,我们发现开放最多做 $6$ 次,那么我们可以以退为进,直接暴力,然后如果不是 $1$ 或者 $0$ 就开放。

线段树 2 代码

#include <bits/stdc++.h>

using namespace std;
/*
*/
#define int long long
int n, m, mod;
int a[1000005], seg[1000005], mult[1000005], addt[1000005];
void build(int l, int r, int nw) {
    mult[nw] = 1;
    if (l == r) {
        seg[nw] = a[l];
        return;
    }
    int mid = (l + r) / 2;
    build(l, mid, nw * 2);
    build(mid + 1, r, nw * 2 + 1);
    seg[nw] = (seg[nw * 2] + seg[nw * 2 + 1]) % mod;
}
void push_down(int len, int nw) {
    mult[nw * 2] = (mult[nw] * mult[nw * 2]) % mod;
    mult[nw * 2 + 1] = (mult[nw] * mult[nw * 2 + 1]) % mod;
    addt[nw * 2] = (mult[nw] * addt[nw * 2]) % mod;
    addt[nw * 2 + 1] = (mult[nw] * addt[nw * 2 + 1]) % mod;
    seg[nw * 2] = (seg[nw * 2] * mult[nw]) % mod;
    seg[nw * 2 + 1] = (seg[nw * 2 + 1] * mult[nw]) % mod;
    mult[nw] = 1;
    addt[nw * 2] = (addt[nw] + addt[nw * 2]) % mod;
    addt[nw * 2 + 1] = (addt[nw] + addt[nw * 2 + 1]) % mod;
    seg[nw * 2] = (seg[nw * 2] + addt[nw] * (len - len / 2)) % mod;
    seg[nw * 2 + 1] = (seg[nw * 2 + 1] + addt[nw] * (len / 2)) % mod;
    addt[nw] = 0;
}
void add(int l, int r, int nl, int nr, int nw, int z) {

    if (nr < l || nl > r) return;

    if (nr <= r && nl >= l) {
        seg[nw] = ((nr - nl + 1) * z + seg[nw]) % mod;
        addt[nw] = (z + addt[nw]) % mod;
        return;
    }

    if (addt[nw] || mult[nw] != 1) push_down(nr - nl + 1, nw);
    int mid = (nl + nr) / 2;
    add(l, r, nl, mid, nw * 2, z);
    add(l, r, mid + 1, nr, nw * 2 + 1, z);
    seg[nw] = (seg[nw * 2] + seg[nw * 2 + 1]) % mod;
}
void mul(int l, int r, int nl, int nr, int nw, int z) {

    if (nr < l || nl > r) return;

    if (nr <= r && nl >= l) {
        seg[nw] = (seg[nw] * z) % mod;
        // if (nl < nr) {
            mult[nw] = (mult[nw] * z) % mod;
            addt[nw] = (addt[nw] * z) % mod;
        // }
        return;
    }

    if (addt[nw] || mult[nw] != 1)push_down(nr - nl + 1, nw);
    int mid = (nl + nr) / 2;
    mul(l, r, nl, mid, nw * 2, z);
    mul(l, r, mid + 1, nr, nw * 2 + 1, z);
    seg[nw] = (seg[nw * 2] + seg[nw * 2 + 1]) % mod;
}
int query(int l, int r, int nl, int nr, int nw) {
    // cout << l << " "  << r << " " << nl << " " << nr << " " << nw << " " << seg[nw] << "\n";
    if (nr < l || nl > r) return 0;

    if (nr <= r && nl >= l) {
        return seg[nw];
    } 

    if (addt[nw] || mult[nw] != 1) push_down(nr - nl + 1, nw);
    int mid = (nl + nr) / 2;
    int lc = query(l, r, nl, mid, nw * 2) % mod;
    int rc = query(l, r, mid + 1, nr, nw * 2 + 1) % mod;
    return (lc + rc) % mod;
}
signed main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> m >> mod;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
    }
    // cout << 1;
    build(1, n, 1);

    for (int i = 1; i <= m; i ++) {
        int op; cin >> op;
        //         for (int i = 1; seg[i]; i ++) {
        //     cout << seg[i] << " ";
        // }
        // cout << "\n";
        if (op == 1) {
            int x, y, k; cin >> x >> y >> k;
            mul(x, y, 1, n, 1, k);
        } else if (op == 2) {
            int x, y, k; cin >> x >> y >> k;
            add(x, y, 1, n, 1, k);
        } else {
            int x,y;
            cin >> x >> y;
            cout << query(x, y, 1, n, 1) % mod << "\n";
        }

    }
    return 0;
}

GSS4 - Can you answer these queries IV - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <bits/stdc++.h>

using namespace std;
/*
*/
#define int long long
int n, m;
const int mod =  1e9+7;int a[1000005], seg[1000005], lazy[1000005], seg2[1000005], tag[1000005];
void push_up(int nw) {
    seg[nw] = seg[nw * 2] + seg[nw * 2 + 1];
    tag[nw] = max(tag[nw * 2], tag[nw * 2 + 1]);
}

    void build(int l, int r, int nw) {
        if (l == r) {
            seg[nw] = tag[nw] = a[l];
            //Leaf node.
            // cout << "[Debug] Finded leaf node " << nw << "\n";
            return; 
        }
        int mid = (l + r) / 2;
//        cout << "11\n";
        build(l, mid, nw * 2);
        build(mid + 1, r, nw * 2 + 1);
        // seg[nw] = seg[nw * 2] + seg[nw * 2 + 1];
        push_up(nw);
        //update
    }

void sqr(int l, int r, int nl, int nr, int nw) {
    // cout << nl << " " << nr << " " << seg[nw] << "\n";
    if (nr < l || nl > r) return;
    if (nl == nr) {
        seg[nw] = floor(sqrt(seg[nw]));

        tag[nw] = seg[nw];
        return;
    }

    int mid = (nl + nr) / 2;
    if (tag[nw * 2] > 1) sqr(l, r, nl, mid, nw * 2);
    if (tag[nw * 2 + 1] > 1) sqr(l, r, mid + 1, nr, nw * 2 + 1);
    // seg[nw] = seg[nw * 2] + seg[nw * 2 + 1];
    push_up(nw);
}
int query(int l, int r, int nl, int nr, int nw) {
    // cout << l << " "  << r << " " << nl << " " << nr << " " << nw << " " << seg[nw] << "\n";
    // if (nr < l || nl > r) return 0;

    if (nr <= r && nl >= l) {
        return seg[nw];
    } 
    int mid = (nl + nr) / 2;
    int lc = 0, rc = 0;
    if (l <= mid) lc = query(l, r, nl, mid, nw * 2);
    if (r > mid) rc = query(l, r, mid + 1, nr, nw * 2 + 1);
    return lc + rc;
    push_up(nw);
}
void printseg() {
    for (int i = 1; seg[i]; i ++) cout << tag[i] << " ";
    cout << "\n";
}
void solve() {

    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
    }
    build(1, n, 1);
        cin >> m;
        // cout << m << "\n";
    for (int i = 1; i <= m; i ++) {
        int op; cin >> op;

        if (op == 0) {
            int x, y, z; cin >> x>> y;
            if (x > y) swap(x, y);
            sqr(x, y, 1, n, 1);
        } else{
            int x, y; cin >> x >> y;
            if (x > y) swap(x, y);
            cout << query(x, y, 1, n, 1) << "\n";
        }
        // printseg();
    }
}
int cnt = 0;
signed main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    while (cin >> n) {
        cout << "Case #" << ++cnt << ":\n";
        solve();
        cout << "\n";
    }
}

继续阅读

评论 (0)

取消