1. 程式人生 > 實用技巧 >Loj 2980. 「THUSCH 2017」大魔法師 線段樹維護矩陣

Loj 2980. 「THUSCH 2017」大魔法師 線段樹維護矩陣

Loj 2980. 「THUSCH 2017」大魔法師

線段樹維護矩陣。

​ 可以對每個節點維護這樣一個矩陣:\(\begin{bmatrix} A\\B\\C\\1 \end{bmatrix}\)

​ 為啥要多個1呢?因為會有\(A += v,C = v\)的操作,這樣好轉移。

​ 當\(opt == 1\)時,轉移矩陣為:\(\begin{bmatrix} 1&1&0&0 \\ 0&1&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}\)

​ 當\(opt == 2,3\)時,轉移矩陣與1類似。

​ 當\(opt == 4\)

時,轉移矩陣為:\(\begin{bmatrix} 1&0&0&v \\ 0&1&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}\)

​ 當\(opt == 5\)時,轉移矩陣為:\(\begin{bmatrix} 1&0&0&0 \\ 0&v&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix}\)

​ 當\(opt == 6\)時,轉移矩陣為:\(\begin{bmatrix} 1&0&0&0 \\ 0&1&0&0\\0&0&0&v\\0&0&0&1\end{bmatrix}\)

#include <bits/stdc++.h>

#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 2.5e5 + 5, mod = 998244353;
int n, m, ansa, ansb, ansc;
struct mat {
    int v[4][4];
    mat() { memset(v, 0, sizeof(v)); }
    friend mat operator * (const mat &a, const mat &b) {
        mat c;
        for(int i = 0;i <= 3; i++) for(int j = 0;j <= 3; j++) c.v[i][j] = 0;
        for(int i = 0;i <= 3; i++) 
            for(int j = 0;j <= 3; j++)
                for(int k = 0;k <= 3; k++) 
                    if(a.v[i][k] && b.v[k][j]) //減小常數
                        c.v[i][j] = (c.v[i][j] + 1ll * a.v[i][k] * b.v[k][j] % mod) % mod;
        return c;
    }
} orz, turn;
struct tree { mat sum, tag; } t[N << 2]; 

void up(int o) {
    for(int i = 0;i <= 3; i++)
        t[o].sum.v[i][0] = (t[ls(o)].sum.v[i][0] + t[rs(o)].sum.v[i][0]) % mod;
}
 
void build(int o, int l, int r) {
    t[o].tag = orz;
    if(l == r) {
        t[o].sum.v[0][0] = read(); t[o].sum.v[1][0] = read();
        t[o].sum.v[2][0] = read(); t[o].sum.v[3][0] = 1;
        return ;
    }
    build(ls(o), l, mid); build(rs(o), mid + 1, r);
    up(o);
}

void down(int o) {
    t[ls(o)].tag = t[o].tag * t[ls(o)].tag; //這裡注意順序別搞反
    t[ls(o)].sum = t[o].tag * t[ls(o)].sum;
    t[rs(o)].tag = t[o].tag * t[rs(o)].tag;
    t[rs(o)].sum = t[o].tag * t[rs(o)].sum;
    t[o].tag = orz;
}

void change(int o, int l, int r, int x, int y) {
    if(x <= l && y >= r) {
        t[o].tag = turn * t[o].tag; //這裡注意順序別搞反
        t[o].sum = turn * t[o].sum;
        return ;
    }
    down(o);
    if(x <= mid) change(ls(o), l, mid, x, y);
    if(y > mid) change(rs(o), mid + 1, r, x, y);
    up(o);
}

void query(int o, int l, int r, int x, int y) {
    if(x <= l && y >= r) {
        ansa += t[o].sum.v[0][0]; ansb += t[o].sum.v[1][0]; ansc += t[o].sum.v[2][0]; 
        ansa %= mod; ansb %= mod; ansc %= mod; return ;
    } 
    down(o);
    if(x <= mid) query(ls(o), l, mid, x, y);
    if(y > mid) query(rs(o), mid + 1, r, x, y);
}

int main() {

    n = read(); 
    for(int i = 0;i <= 3; i++) orz.v[i][i] = 1; //單位矩陣
    build(1, 1, n);
    m = read();
    for(int i = 1, opt, x, y, v;i <= m; i++) {
        opt = read(); x = read(); y = read(); 
        if(opt >= 4 && opt <= 6) v = read();
        turn = orz;
        if(opt == 1) turn.v[0][1] = 1, change(1, 1, n, x, y); 
        if(opt == 2) turn.v[1][2] = 1, change(1, 1, n, x, y); 
        if(opt == 3) turn.v[2][0] = 1, change(1, 1, n, x, y); 
        if(opt == 4) turn.v[0][3] = v, change(1, 1, n, x, y); 
        if(opt == 5) turn.v[1][1] = v, change(1, 1, n, x, y); 
        if(opt == 6) turn.v[2][2] = 0, turn.v[2][3] = v, change(1, 1, n, x, y); 
        if(opt == 7) ansa = ansb = ansc = 0, query(1, 1, n, x, y), printf("%d %d %d\n", ansa, ansb, ansc);
    }

    return 0;
}