1. 程式人生 > 其它 >AcWing 1277. 維護序列

AcWing 1277. 維護序列

題目傳送門

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 100010;
/*
1.區間修改 - 乘 加
3.區間查詢

儲存資訊:
1.區間範圍l,r
2.加的懶標記add
3.乘的懶標記mul
4.區間總和sum

對於x
若對懶標記的處理是先加再乘
    若此次操作為乘上一個數c
        可以表示為 (n + add) * mul * c 即 (n + X) * X 的形式
    若此次操作為加上一個數c
        (n + add) * mul + c 不能寫成 (n + X ) * X的形式
        -> 無法更新新的懶標記

若對懶標記的處理是先乘再加
    若此次操作是加上一個數c
        可以表示為n * mul + add + c
        -> 此時新的add即為add + c
    若此次操作是乘上一個數c
        可以表示為n * mul * c + add * c
        -> 此時新的add即為add * c,新的mul即為mul * c

-> 故先乘再加,以便更新懶標記

可以把乘和加的操作都看成 x * c + d
    -> 若是乘法,d為0
    -> 若是加法,c為1

若當前x的懶標記為add和mul
    -> 操作可以寫成(x * mul + add) * c + d
    -> 即x * (mul * c) + (add * c + d)
    -> 新的mul為(mul * c),新的add為(add * c + d)

注意:乘的懶標記初始為1
*/
// n:N 個非負整數   p:數模 P 的值  m:操作總數
int n, p, m;
int w[N];
struct Node {
    int l, r;
    int sum, add, mul;
} tr[N * 4];

//子節點更新父節點統計資訊
void pushup(int u) {
    tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p; //更新sum和資訊
}

//計算函式
void eval(Node &t, int add, int mul) {
    t.sum = ((LL)t.sum * mul + (LL)(t.r - t.l + 1) * add) % p;
    t.mul = (LL)t.mul * mul % p;
    t.add = ((LL)t.add * mul + add) % p;
}

void pushdown(int u) {
    eval(tr[u << 1], tr[u].add, tr[u].mul);     //向左兒子推送資訊
    eval(tr[u << 1 | 1], tr[u].add, tr[u].mul); //向右兒子推送資訊
    tr[u].add = 0, tr[u].mul = 1;               //清空懶標記
}

//構建
void build(int u, int l, int r) {
    if (l == r) {
        tr[u] = {l, r, w[r], 0, 1}; // sum=w[r],add=0,mul=1
        return;
    }
    tr[u] = {l, r, 0, 0, 1}; // sum=0:現在無法計算出來,需要等待兒孫們pushup更新 add=0,mul=1理解為預設值
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    //子節點建立完,需要更新父節點,add,mul其實不用更新,是懶標記,但sum是需要更新的
    pushup(u);
}

//修改區間
void modify(int u, int l, int r, int add, int mul) {
    if (tr[u].l >= l && tr[u].r <= r)
        eval(tr[u], add, mul);
    else {
        pushdown(u); //向下更新一下懶標記

        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, add, mul);
        if (r > mid) modify(u << 1 | 1, l, r, add, mul);
        //更新一下統計資訊
        pushup(u);
    }
}

int query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;

    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    int sum = 0;
    if (l <= mid) sum = query(u << 1, l, r);
    if (r > mid) sum = (sum + query(u << 1 | 1, l, r)) % p;
    return sum;
}

int main() {
    cin >> n >> p;
    for (int i = 1; i <= n; i++) cin >> w[i];

    //構建線段樹,root=1,l=1,r=n
    build(1, 1, n);

    cin >> m;
    while (m--) {
        int t, l, r, d;
        cin >> t >> l >> r;
        if (t == 1) { //乘
            cin >> d;
            modify(1, l, r, 0, d);
        } else if (t == 2) { //加
            cin >> d;
            modify(1, l, r, d, 1);
        } else //查
            printf("%d\n", query(1, l, r));
    }
    return 0;
}