题目链接

题目

题目描述

\(Keven\) 特别喜欢线段树,他给你一个长度为 \(n\) 的序列,对序列进行 \(m\) 次操作。

操作有两种:

1 \(1\ l\ r\ k\) :表示将下标在 \([l , r]\) 区间内的数字替换成 \([k,k+1,…,k+r-l]\)

\(2\ l\ r\) :表示查询区间 \([l , r]\) 的区间和

输入描述

第一行两个整数 \(n、m\) ,表示序列的长度和操作次数 \((1 \leq n,m \leq 2 \times 10^5)\) 第二行 \(n\) 个整数,表示序列的初始值 \(a_1,a_2,…a_n(1 \leq a_i \leq 2 \times 10^5)\) 接下来 \(m\) 行,每行三或四个数字,若第一个数字是 \(1\) ,则表示操作 \(1\) ,反之则表示操作 \(2\)\((1 \leq l \leq r \leq n,1 \leq k \leq 2e5)\)

输出描述

对于每个操作 \(2\) ,输出一行一个整数表示区间和。

示例1

输入

5 5
1 1 1 1 1
2 1 5
1 1 5 1
2 1 5
1 1 3 3
2 1 3

输出

5
15
12

说明

第一次1操作后,序列是1 2 3 4 5
第二次1操作后,序列是3 4 5 4 5

题解

知识点:线段树,数学。

本题的区间修改不具有区间无关性,因为在一次修改的不同的子区间,修改维护的值是不同的,因此需要在 update 函数内做特殊处理,是非标准的线段树。

区间信息需要维护区间长度 \(len\) 、区间和 \(sum\) ,区间合并直接加即可。

区间修改需要维护数值起始点 \(k\) ,新区间和可以直接公式求出 \(\dfrac{(2k + len - 1)len}{2}\)

区间修改可以设置懒标记,只需要记录最新一次的修改即可。

注意,要考虑标记的单位元值,即表示未修改过的标记值,这里取 \(-1\) 。在 push_down 函数中需先判断标记是否为 \(-1\) ,再进行下传。

区间更新函数有两种写法:

  1. 不更改修改值,在更新时根据子区间位置直接求出实际修改值。
void update(int rt, int l, int r, int x, int y, F f) {
    if (r < x || y < l) return;
    if (x <= l && r <= y) return node[rt] = F{ f.k + l - x }(node[rt]), lazy[rt] = F{ f.k + l - x }(lazy[rt]), void();
    push_down(rt);
    int mid = l + r >> 1;
    update(rt << 1, l, mid, x, y, f);
    update(rt << 1 | 1, mid + 1, r, x, y, f);
    node[rt] = node[rt << 1] + node[rt << 1 | 1];
}
  1. 每次更新子区间时,同时更改相应的修改值。注意,这种方法需要控制更新的子区间,否则无法计算相应的修改值。
void update(int rt, int l, int r, int x, int y, F f) {
    if (x == l && r == y) return node[rt] = f(node[rt]), lazy[rt] = f(lazy[rt]), void();
    push_down(rt);
    int mid = l + r >> 1;
    if (y <= mid) update(rt << 1, l, mid, x, y, f);
    else if (x >= mid + 1) update(rt << 1 | 1, mid + 1, r, x, y, f);
    else update(rt << 1, l, mid, x, mid, f), update(rt << 1 | 1, mid + 1, r, mid + 1, y, { f.k + mid + 1 - x });
    node[rt] = node[rt << 1] + node[rt << 1 | 1];
}

实际上,后者精确控制更新区间的方法,在遇到其他因为修改与区间位置有关等问题,需要魔改 update 函数时,更好用。

时间复杂度 \(O((n+m)\log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

struct T {
    int len;
    ll sum;
    static T e() { return { 0,0 }; }
    friend T operator+(const T &a, const T &b) { return { a.len + b.len,a.sum + b.sum }; }
};
struct F {
    int k;
    static F e() { return{ -1 }; }
    T operator()(const T &x) { return { x.len, (2LL * k + x.len - 1) * x.len / 2 }; }
    F operator()(const F &g) { return { k }; }
};
class SegmentTreeLazy {
    int n;
    vector<T> node;
    vector<F> lazy;

    void push_down(int rt) {
        if (!~lazy[rt].k) return;
        node[rt << 1] = lazy[rt](node[rt << 1]);
        lazy[rt << 1] = lazy[rt](lazy[rt << 1]);
        lazy[rt].k += node[rt << 1].len;
        node[rt << 1 | 1] = lazy[rt](node[rt << 1 | 1]);
        lazy[rt << 1 | 1] = lazy[rt](lazy[rt << 1 | 1]);
        lazy[rt] = F::e();
    }

    /* void update(int rt, int l, int r, int x, int y, F f) {
        if (r < x || y < l) return;
        if (x <= l && r <= y) return node[rt] = F{ f.k + l - x }(node[rt]), lazy[rt] = F{ f.k + l - x }(lazy[rt]), void();
        push_down(rt);
        int mid = l + r >> 1;
        update(rt << 1, l, mid, x, y, f);
        update(rt << 1 | 1, mid + 1, r, x, y, f);
        node[rt] = node[rt << 1] + node[rt << 1 | 1];
    } */

    void update(int rt, int l, int r, int x, int y, F f) {
        if (x == l && r == y) return node[rt] = f(node[rt]), lazy[rt] = f(lazy[rt]), void();
        push_down(rt);
        int mid = l + r >> 1;
        if (y <= mid) update(rt << 1, l, mid, x, y, f);
        else if (x >= mid + 1) update(rt << 1 | 1, mid + 1, r, x, y, f);
        else update(rt << 1, l, mid, x, mid, f), update(rt << 1 | 1, mid + 1, r, mid + 1, y, { f.k + mid + 1 - x });
        node[rt] = node[rt << 1] + node[rt << 1 | 1];
    }

    T query(int rt, int l, int r, int x, int y) {
        if (r < x || y < l) return T::e();
        if (x <= l && r <= y) return node[rt];
        push_down(rt);
        int mid = l + r >> 1;
        return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
    }
public:
    SegmentTreeLazy(int _n = 0) { init(_n); }
    SegmentTreeLazy(const vector<T> &src) { init(src); }

    void init(int _n) {
        n = _n;
        node.assign(n << 2, T::e());
        lazy.assign(n << 2, F::e());
    }
    void init(const vector<T> &src) {
        assert(src.size());
        init(src.size() - 1);
        function<void(int, int, int)> build = [&](int rt, int l, int r) {
            if (l == r) return node[rt] = src[l], void();
            int mid = l + r >> 1;
            build(rt << 1, l, mid);
            build(rt << 1 | 1, mid + 1, r);
            node[rt] = node[rt << 1] + node[rt << 1 | 1];
        };
        build(1, 1, n);
    }

    void update(int x, int y, F f) { update(1, 1, n, x, y, f); }

    T query(int x, int y) { return query(1, 1, n, x, y); }
};

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    vector<T> a(n + 1);
    for (int i = 1;i <= n;i++) {
        int x;
        cin >> x;
        a[i] = { 1,x };
    }
    SegmentTreeLazy sgt(a);
    while (m--) {
        int op, l, r;
        cin >> op >> l >> r;
        if (op == 1) {
            int k;
            cin >> k;
            sgt.update(l, r, { k });
        }
        else cout << sgt.query(l, r).sum << '\n';
    }
    return 0;
}