首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

UVA 11992 Fast Matrix Operations:二維線段樹/一維化區間加蓋操作

     編輯:關於C++

問題描述

矩陣最大 10^6 元素,最多 20 行。支持四種操作(m ≤ 20000):

  1. 區間加:子矩陣每個元素 +v
  2. 區間賦值:子矩陣每個元素 =v
  3. 區間查詢:子矩陣求和、最小值、最大值

關鍵觀察:行數少(≤20)

將二維矩陣按行展平為一維數組:第 i 行第 j 列映射到位置 (i-1)*c + j。子矩陣 (x1,y1,x2,y2) 變為 x2-x1+1 個連續區間 [(i-1)*c+y1, (i-1)*c+y2]。

線段樹支持:區間加 + 區間賦值 + 區間查詢

需同時維護 sum、min、max,且賦值操作優先級高於加法(賦值清除舊加法標記)。

節點結構

struct Node {
    ll sum, mn, mx;
    ll add_tag;   // 區間加標記
    ll set_tag;   // 區間賦值標記,-1 表示無
    bool has_set() { return set_tag != -1; }
};

關鍵操作

  • PushDown:先推賦值標記(覆蓋子節點的加/賦值),再推加法標記。
  • Maintain:根據子節點與當前標記更新 sum/mn/mx。
  • UpdateAdd / UpdateSet:標準區間更新。
  • Query:遞歸收集 sum/mn/mx,傳遞累積加法標記。

完整代碼框架

class SegTree {
    vector t;
    int n;
    void apply_set(int o, int l, int r, ll v) { ... }
    void apply_add(int o, int l, int r, ll v) { ... }
    void push_down(int o, int l, int r) { ... }
    void maintain(int o, int l, int r) { ... }
    void update_add(int o, int l, int r, int L, int R, ll v) { ... }
    void update_set(int o, int l, int r, int L, int R, ll v) { ... }
    void query(int o, int l, int r, int L, int R, ll add_acc, Res& res) { ... }
public:
    SegTree(int _n) : n(_n) { t.resize(4*n); build(1,1,n); }
    void add(int l, int r, ll v) { update_add(1,1,n,l,r,v); }
    void set(int l, int r, ll v) { update_set(1,1,n,l,r,v); }
    Res ask(int l, int r) { Res res; query(1,1,n,l,r,0,res); return res; }
};

主程序處理

while (scanf("%d%d%d", &R, &C, &M) == 3) {
    SegTree st(R * C);
    for (int q = 0; q < M; ++q) {
        int op, x1, y1, x2, y2; ll v;
        scanf("%d%d%d%d%d", &op, &x1, &y1, &x2, &y2);
        if (op <= 2) scanf("%lld", &v);
        if (op == 1) {
            for (int i = x1; i <= x2; ++i) st.add((i-1)*C+y1, (i-1)*C+y2, v);
        } else if (op == 2) {
            for (int i = x1; i <= x2; ++i) st.set((i-1)*C+y1, (i-1)*C+y2, v);
        } else {
            ll sum = 0, mn = INF, mx = -INF;
            for (int i = x1; i <= x2; ++i) {
                Res r = st.ask((i-1)*C+y1, (i-1)*C+y2);
                sum += r.sum; mn = min(mn, r.mn); mx = max(mx, r.mx);
            }
            printf("%lld %lld %lld\n", sum, mn, mx);
        }
    }
}

複雜度分析

  • 展平後數組長 N = R*C ≤ 10^6。
  • 單次操作最多橫跨 20 行 → 20 次線段樹操作。
  • 每次線段樹操作 O(log N)。
  • 總時間 O(M * R * log N) ≤ 20000 * 20 * 20 ≈ 8×10^6,極快。
  • 空間 O(N) 線段樹節點約 4M,可接受。

總結

「行數少」是突破口,將二維區間操作轉化為多次一維區間操作。線段樹需正確處理賦值標記優先級高於加法標記的覆蓋語義,PushDown 時先推賦值再推加法。此題是「區間加 + 區間賦值 + 區間查詢」線段樹的經典實戰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved