[洛谷P2023, AHOI2009]維護序列

這是一道典型的線段樹板子題. hzwer說過, 對於線段樹的多標記 lazy-tag 可以根據標記的優先級, 優先級高的先下傳.

但是我一直思路有點問題, 比如說這道題.

這道題要求的是區間加法, 區間乘法, 要查詢的是區間和.

我開始的想法是, 不管是加法標記還是乘法標記都是相斥的, 要進行加法, 就必須把乘法標記清零(其實是清一),

要進行乘法, 就必須把加法標記清零.

這樣想的話, 無法在均攤logn的時間下, 完成RMQ. 期間還想了加法標記使用標記永久化, 乘法標記使用延遲下傳.

後來自己寫了個代碼框架, 就看了別人的題解. 觀後發現確實是乘法的優先級要比加法的高.

主要操作大致是這樣的, 對於加法操作, 對包含的區間直接把加法標記加上即可.

對於乘法操作, 對包含的區間的乘法標記和加法標記都要乘上新的乘法標記. 可能代碼更加直觀.

比如說 left 左兒子的標記爲 * a + b, 此時傳遞一個mutiTag c, 那麼 left 的標記就會變成 * a * c + b * c.

還有就是, 對於幺元, 加法是零, 乘法是一.

在 build函數中, 要把所有區間的addTag都設爲 0 , mutiTag都設爲 1 .

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
const int MAXN = 1e5 + 600;
typedef long long ll;
using namespace std;
int n, m;
int cmd, ql, qh;
ll v, MOD;
struct Segment{
    ll sum, addTag, mutiTag;
} s[MAXN << 2 | 1];
inline void spread(int o, int lo, int hi){
    int left = o << 1, right = o << 1 | 1;
    if(s[o].mutiTag != 1){// 先傳遞 mutiTag
        s[left].sum = (s[left].sum * s[o].mutiTag) % MOD;
        s[right].sum = (s[right].sum * s[o].mutiTag) % MOD;

        s[left].mutiTag = (s[left].mutiTag * s[o].mutiTag) % MOD;
        s[right].mutiTag = (s[right].mutiTag * s[o].mutiTag) % MOD;

        s[left].addTag = (s[left].addTag * s[o].mutiTag) % MOD;
        s[right].addTag = (s[right].addTag * s[o].mutiTag) % MOD;

        s[o].mutiTag = 1;
    }
    if(s[o].addTag){// 再傳遞 addTag
        int mi = (lo + hi) >> 1;
        int leftSize = mi - lo, rightSize = hi - mi;

        s[left].sum = (s[left].sum + (leftSize * s[o].addTag)) % MOD;
        s[right].sum = (s[right].sum + (rightSize * s[o].addTag)) % MOD;

        s[left].addTag = (s[left].addTag + s[o].addTag) % MOD;
        s[right].addTag = (s[right].addTag + s[o].addTag) % MOD;

        s[o].addTag = 0;
    }
}
inline void update(int o){
    s[o].sum = (s[o << 1].sum + s[o << 1 | 1].sum) % MOD;
}
void build(int o, int lo, int hi){
    if(hi - lo < 2){
        scanf("%lld", &s[o].sum);
        s[o].sum = s[o].sum % MOD;

        return;
    }
    int mi = (lo + hi) >> 1;
    build(o << 1, lo, mi);
    build(o << 1 | 1, mi, hi);

    update(o);
    s[o].addTag = 0;
    s[o].mutiTag = 1;
}
void rangeAdd(int o, int lo, int hi){
    if(qh <= lo || hi <= ql){// 完全不在查詢區間的, 最先判斷.
        return;
    }
    if(ql <= lo && hi <= qh){
        s[o].addTag += v;
        s[o].sum += (v * (hi - lo)) % MOD;
        s[o].sum %= MOD;
        return;
    }

    spread(o, lo, hi);
    int mi = (lo + hi) >> 1;
    rangeAdd(o << 1, lo, mi);
    rangeAdd(o << 1 | 1, mi, hi);

    update(o);
}
void rangeMuti(int o, int lo, int hi){
    if(qh <= lo || hi <= ql){
        return;
    }
    if(ql <= lo && hi <= qh){
        // spread(o, lo, hi);
        s[o].mutiTag = (s[o].mutiTag * v) % MOD;
        s[o].addTag = (s[o].addTag * v) % MOD;
        s[o].sum = (s[o].sum * v) % MOD;
        return;
    }

    spread(o, lo, hi);
    int mi = (lo + hi) >> 1;
    rangeMuti(o << 1, lo, mi);
    rangeMuti(o << 1 | 1, mi, hi);

    update(o);
}
ll query(int o, int lo, int hi){
    if(qh <= lo || hi <= ql){
        return 0;
    }
    if(ql <= lo && hi <= qh){
        return s[o].sum;
    }

    spread(o, lo, hi);
    int mi = (lo + hi) >> 1;
    ll rst = 0;
    rst = (query(o << 1, lo, mi) + query(o << 1 | 1, mi, hi)) % MOD;
    return rst;
}
void print(int o, int lo, int hi){
    if(hi - lo < 2){
        printf(" %d-%lld", lo, s[o].sum);
        return;
    }
    spread(o, lo, hi);
    int mi = (lo + hi) >> 1;
    print(o << 1, lo, mi);
    print(o << 1 | 1, mi, hi);
}
int main(){
    scanf("%d%lld", &n, &MOD);
    build(1, 1, ++n);
    // print(1, 1, n), puts("\tTEST\n");// print調試, 非常好用.
    scanf("%d", &m);
    for(int i = 0; i < m; ++i){
        scanf("%d%d%d", &cmd, &ql, &qh);
        ++qh;
        switch(cmd){
            case 1:
                scanf("%lld", &v);
                rangeMuti(1, 1, n);
                break;
            case 2:
                scanf("%lld", &v);
                rangeAdd(1, 1, n);
                break;
            case 3:
                printf("%lld\n", query(1, 1, n));
                break;
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章