线段树->线段树2(洛谷3373)

题意:n个数m个查询,三种操作。 1:区间的数都+k。2:区间的数都*k。3:求区间和

分析:考虑如何设计区间更新规则。 对于lazy的更新:如果乘进来,那么和跟乘积都乘,然后和加上和。如果和进来直接加。如果同时进来,也是先乘再加。 对于区间sum的更新,按照先乘再加即可。 (突然发现lazy标记的更新好像跟区间update的更新一样,不过update是更新的查询结果,而lazy是更新合并区间更改的值)

long long mod;


struct Node{
    long long sum;
    long long add;
    long long mul;
    bool is_lazy = false;

    Node(bool state = false): sum(0ll), add(0ll), mul(1ll), is_lazy(state){}

    //合并区间
    Node operator +(const Node& other){
        Node rst = *this;
        rst.sum = (rst.sum + other.sum) % mod;
        return rst;
    }

    //合并懒标记
    void operator |=(const Node& other){
        mul = mul * other.mul % mod;
        add = (add * other.mul +  other.add) % mod;
        is_lazy = true;
    }

    //这个函数定义如何在区间上设计规则更新懒标记的各种值
    void update(const Node& other, int length = 1){
        long long cur = sum;
        sum = sum * other.mul % mod;
        sum = (sum + length * other.add % mod) % mod;
    }


    bool isLazy()const{
        return is_lazy;
    }
    bool setLazy(){
        is_lazy = true;
    }
};


template<class T>
class SegmentTree{
private:
    vector<T> st_, lazy_;
    int size_;
    const T flag_ = {};

    T conquer(T a, T b){
        return a + b;
    }

    void propagate(int p, int l, int r){
        if (lazy_[p].isLazy()){
            st_[p].update(lazy_[p], r - l + 1);           //update更新该区间跟flag值
            if (l != r){
                lazy_[p << 1] |= lazy_[p];
                lazy_[p << 1 | 1] |= lazy_[p];          // |=    叠加flag懒人标记重载运算符
            }
            lazy_[p] = flag_;
        }

    }

    //以下部分暂时不需要改
    void build(const vector<T>& s, int p, int l, int r){
        if (l == r){
            st_[p] = s[l];
        }
        else{
            int mid = (l + r) >> 1;
            build(s, p << 1, l, mid);
            build(s, p << 1 | 1, mid + 1, r);
            st_[p] = conquer(st_[p << 1], st_[p << 1 | 1]);
        }
    }

    void update(int p, int l, int r, int i, int j, T val){
        propagate(p, l, r);     //必须放在if语句前,因为如果是子区间要被上一层递归调用合并
        if (i > j) {
            return;
        }
        if (l >= i && r <= j){
            lazy_[p] = val;
            propagate(p, l, r);     //更新st[p],并将lazy值往下传递一层
        }
        else{
            int mid = (l + r) >> 1;
            update(p << 1, l, mid, i, std::min(mid, j), val);
            update(p << 1 | 1, mid + 1, r, std::max(i, mid + 1), j, val);
            st_[p] = conquer(st_[p << 1], st_[p << 1 | 1]);

        }
    }

    //如果把语句改成这样,好像就不需要flag来判断非法了。所有返回值均合法。
    T query(int p, int l, int r, int i, int j){
        propagate(p, l, r);
        if (l >= i && r <= j){
            return st_[p];
        }
        int mid = (l + r) >> 1;
        if (j <= mid){
            return query(p << 1, l, mid, i, j);
        }
        if (i > mid){
            return query(p << 1 | 1, mid + 1, r, i, j);
        }
        return conquer(query(p << 1, l, mid, i, j), query(p << 1 | 1, mid + 1, r, i, j));
    }


public:
    explicit SegmentTree(int sz): size_(sz){       //这里有多少传多少,不要传n + 1,n代表具体的数字数量,不代表下标。 BIT, dsu 代表的是下标。
        st_.resize(4 * size_);
        lazy_.assign(4 * size_, flag_);
    }

    explicit SegmentTree(vector<T>& a):SegmentTree(int(a.size()) - 1){     //约定下标从1开始,那么这里一定a.size() -1,不然会导致计算结果错误。
        build(a, 1, 1, size_);
    }


    void update(int i, int j, T val){
        update(1, 1, size_, i, j, val);
    }

    T query(int i, int j){
        return query(1, 1, size_, i, j);
    }

};

void solve(){
    int n, m;
    cin >> n >> m >> mod;

    vector<Node> a(n + 1);
    for (int i = 1; i <= n; ++i){
        cin >> a[i].sum;
    }

    SegmentTree<Node> st(a);

    while (m --){
        int t, l, r;
        cin >> t >> l >> r;
        if (t == 1){
            long long x;
            cin >> x;
            Node temp{true};
            temp.mul = x;
            st.update(l, r, temp);
        }
        else if (t == 2){
            long long x;
            cin >> x;
            Node temp{true};
            temp.add = x;
            st.update(l, r, temp);
        }
        else{
            cout << st.query(l, r).sum << '\n';
        }
    }
}
posted @ 2024-01-11 22:35  _Yxc  阅读(15)  评论(0)    收藏  举报