luogu3373线段树2..支持区间加值和乘值的线段树

Posted hnfms-jerry

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu3373线段树2..支持区间加值和乘值的线段树相关的知识,希望对你有一定的参考价值。

这道题调了2个多小时发现有个地方模运算符号弄错了...

题面:luogu3373
和线段树1一样,这道题也要用Lazy标记思想,这个很好理解.

不过重点在更新的顺序上面:

因为乘法优先,所以每次执行PushDown操作时要先算乘法.
再就是注意到对于乘法的标记,每次要更新的不仅是乘法的,还有加法的标记.

另外,一定要注意模运算的符号!!!

点我看代码

#include <cstdio>
using namespace std;

typedef long long ll;
const int MAXN = 1e5;
struct SegmentTree {
    ll mod, N, su[MAXN * 4 + 10], ad[MAXN * 4 + 10], ml[MAXN * 4 + 10];
    void init() {
        for(int i = 1; i <= N * 4; ++i)
            ml[i] = 1;
    }
    void PushDownPls(ll pos, ll numl, ll numr) {
        if(ad[pos] == 0) return;
        ad[pos << 1] = (ad[pos << 1] + ad[pos]) % mod;
        ad[pos << 1 | 1] = (ad[pos << 1 | 1] + ad[pos]) % mod;
        su[pos << 1] = (su[pos << 1] + ad[pos] * numl % mod) % mod;
        su[pos << 1 | 1] = (su[pos << 1 | 1] + ad[pos] * numr % mod) % mod;
        ad[pos] = 0;
    }
    void PushDownMul(ll pos, ll numl, ll numr) {
        if(ml[pos] == 1) return;
        ml[pos << 1] = (ml[pos << 1] * ml[pos]) % mod;
        ml[pos << 1 | 1] = (ml[pos << 1 | 1] * ml[pos]) % mod;
        su[pos << 1] = (su[pos << 1] * ml[pos]) % mod;
        su[pos << 1 | 1] = (su[pos << 1 | 1] * ml[pos]) % mod;
        ad[pos << 1] = (ad[pos << 1] * ml[pos]) % mod;
        ad[pos << 1 | 1] = (ad[pos << 1 | 1] * ml[pos]) % mod;
        ml[pos] = 1;
    }
    void UpdatePls(ll l, ll r, ll L, ll R, ll val, ll pos) {
        if(L <= l && r <= R) {
            su[pos] = (su[pos] + val * (r - l + 1)) % mod;
            ad[pos] = (ad[pos] + val) % mod;
            return;
        }
        ll m = (l + r) / 2;
        PushDownMul(pos, m - l + 1, r - m);
        PushDownPls(pos, m - l + 1, r - m);
        if(L <= m) UpdatePls(l, m, L, R, val, pos << 1);
        if(R > m) UpdatePls(m + 1, r, L, R, val, pos << 1 | 1);
        su[pos] = (su[pos << 1] + su[pos << 1 | 1]) % mod;
    }
    void UpdateMul(ll l, ll r, ll L, ll R, ll val, ll pos) {
        if(L <= l && r <= R) {
            su[pos] = (su[pos] * val) % mod;
            ml[pos] = (ml[pos] * val) % mod;
            ad[pos] = (ad[pos] * val) % mod;
            return;
        }
        ll m = (l + r) / 2;
        PushDownMul(pos, m - l + 1, r - m);
        PushDownPls(pos, m - l + 1, r - m);
        if(L <= m) UpdateMul(l, m, L, R, val, pos << 1);
        if(R > m) UpdateMul(m + 1, r, L, R, val, pos << 1 | 1);
        su[pos] = (su[pos << 1] + su[pos << 1 | 1]) % mod;
    }
    ll Query(ll l, ll r, ll L, ll R, ll pos) {
        if(L <= l && r <= R)
            return su[pos] % mod;
        ll m = (l + r) / 2, sum = 0;
        PushDownMul(pos, m - l + 1, r - m);
        PushDownPls(pos, m - l + 1, r - m);
        if(L <= m) sum = (sum + Query(l, m, L, R, pos << 1)) % mod;
        if(R > m) sum = (sum + Query(m + 1, r, L, R, pos << 1 | 1)) % mod;
        return sum;
    }
};

ll N, M, K;
SegmentTree st;

int main() {
    scanf("%lld%lld%lld", &N, &M, &K);
    st.mod = K, st.N = N;
    st.init();
    for(ll i = 1, v; i <= N; ++i) {
        scanf("%lld", &v);
        st.UpdatePls(1, N, i, i, v, 1);
    }
    for(ll i = 1, z, l, r, v; i <= M; ++i) {
        scanf("%lld", &z);
        if(z == 1) {
            scanf("%lld%lld%lld", &l, &r, &v);
            st.UpdateMul(1, N, l, r, v, 1);
        } else if(z == 2) {
            scanf("%lld%lld%lld", &l, &r, &v);
            st.UpdatePls(1, N, l, r, v, 1);
        } else if(z == 3) {
            scanf("%lld%lld", &l, &r);
            printf("%lld
", st.Query(1, N, l, r, 1));
        }
    }
    return 0;
}


以上是关于luogu3373线段树2..支持区间加值和乘值的线段树的主要内容,如果未能解决你的问题,请参考以下文章

Luogu 3373模板线段树 2

luogu 3373模板线段树2

luogu P3373 模板线段树 2

Luogu P3373 模板线段树 2

原创洛谷 LUOGU P3373 模板线段树2

Luogu P3373 模板线段树 2