1. 程式人生 > >BZOJ2683: 簡單題(CDQ分治 + 樹狀數組)

BZOJ2683: 簡單題(CDQ分治 + 樹狀數組)

d+ c++ 簡單題 簡單 inline mark fde 格子 不同

BZOJ2683: 簡單題(CDQ分治 + 樹狀數組)
  • 題意:

你有一個\(N*N\)的棋盤,每個格子內有一個整數,初始時的時候全部為\(0\),現在需要維護兩種操作:

命令 參數限制 內容
\(1\ x\ y\ A\) \(1\le x,y \le N\),A是正整數 將格子\(x,y\)裏的數字加上\(A\)
\(2\ x1\ y1\ x2\ y2\) \(1\le x1\le x2\le N,1\le y1\le y2\le N\) 輸出\(x1\ y1\ x2\ y2\)這個矩形內的數字和
\(3\) 終止程序

? \(1<=N<=500000\),操作數不超過\(200000\)

個,內存限制\(20M\)

  • 題解:

這個題是cdq分治的裸題吧。

一維:時間(按輸入順序就行了)

二維:\(x\)坐標(cdq分治)

三維:\(y\)坐標(樹狀數組)

這個題比較裸,但是cdq分治細節還是有一點的(調的錯誤我可以列一版了。。)

?

  • 算法講解:

但我想簡單講一下cdq分治(因為網上很多都很坑沒講清楚)

cdq是專門解決多維偏序的問題,比如像這一道題統計二維矩形的權值,或者直接求高維偏序的個數。

如果不用cdq分治,就只能樹套樹或者KD-tree這種巨型工業數據結構。而且樹套樹常常空間和常數都很恐怖,並且很難寫……

cdq分治是個比較好寫的東西,但其中的思想十分的巧妙和神奇。

你應該學過歸並排序求逆序對吧,那是最裸的cdq了。他就是利用了左邊的答案來更新右邊的答案,cdq就是在這個方面不同於普通的分治。

它每次算答案,只能在右邊區間算也就是\([mid+1,r]\)。這是為什麽可以這樣呢,因為你初始給它的序列,按這樣算的話,絕對只會算它原序列左邊的貢獻,不會算到右邊去。(想一想,為什麽) 這個只需要自己模擬下分治的區間劃分和左右區間考慮就行了。

這就可以會強制使你一開始的那一維有序,對答案計算是正確的。(但切記最後給你的序列不一定是按你給它的順序了!!!)

然後它中間會有一個排序比較的過程,這就可以使第二個維度變得有序了。(最後的序列一定是第二維度有序的) 然後根據前兩個維度算答案就行了,後面的維度全都是附加在這兩個維度上面的。

總的步驟:

  1. 分開(遞歸計算左區間和右區間)
  2. 計算(用左區間來統計,右區間來加上貢獻)
  3. 合並(將當前序列變得有序)
  • 又回到題解:

這道題,就是對於所有操作進行cdq分治(一般都是對於操作進行分治)。

第三維用樹狀數組統計\(y\)的前綴和就行了,因為\(x\)已經排好序了,所以可以直接算了。

左區間只執行Add操作,右區間只執行Sum操作。

對於一個詢問操作,要將它拆成4個詢問操作(就是類似詢問二維前綴和),加加減減就行了。

註意幾個細節(我調了很久的點)

  1. 樹狀數組清空的時候,下標不是val而是y
  2. 拆矩陣的時候,一定要不要寫錯下標;

然後多拍幾組,寫個暴力很容易查出來的。

  • 代碼:
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i)
#define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar() ) if (ch == ‘-‘) fh = -1;
    for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ ‘0‘);
    return x * fh;
}

void File() {
#ifdef zjp_shadow
    freopen ("P2683.in", "r", stdin);
    freopen ("P2683.out", "w", stdout);
#endif
}

int n;

const int N = 800100;
struct Opt {
    int x, y, type, id, val;
    inline bool operator < (const Opt &rhs) const {
        return (x ^ rhs.x) ? x < rhs.x : type < rhs.type;
    }
};
Opt lt[N], tmp[N];

#define lowbit(x) (x & -(x))
struct Fenwick_Tree {
    int c[500100];
    inline void Add(int pos, int val) { for (; pos <= n; pos += lowbit(pos) ) c[pos] += val; }
    inline int Sum(int pos) { int res = 0; for (; pos; pos -= lowbit(pos) ) res += c[pos]; return res; }
    inline void Clear(int pos) { for (; pos <= n; pos += lowbit(pos) ) if (c[pos]) c[pos] = 0; else break; }
};
Fenwick_Tree T;

int ans[N];

void Cdq(int l, int r) {
    if (l == r) return ;
    int mid = (l + r) >> 1;
    Cdq(l, mid); Cdq(mid + 1, r);
    int lp = l, rp = mid + 1, o = l;
    while (lp <= mid && rp <= r) {
        if (lt[lp] < lt[rp]) {
            if (lt[lp].type == 1) T.Add(lt[lp].y, lt[lp].val);
            tmp[o ++] = lt[lp ++];
        } else {
            if (lt[rp].type == 2) ans[lt[rp].id] += lt[rp].val * T.Sum(lt[rp].y);
            tmp[o ++] = lt[rp ++];
        }
    }

    while (lp <= mid) tmp[o ++] = lt[lp ++];
    while (rp <= r) {
        if (lt[rp].type == 2) ans[lt[rp].id] += lt[rp].val * T.Sum(lt[rp].y);
        tmp[o ++] = lt[rp ++];
    }

    For (i, l, mid) if (lt[i].type == 1) T.Clear(lt[i].y);
    For (i, l, r) lt[i] = tmp[i];
}

int qcnt, acnt;
inline void Addq(int x, int y, int type, int id, int val) {
    lt[++qcnt] = (Opt){x, y, type, id, val};
}

int main () {
    File() ;
    n = read();
    for (;;) {
        int opt = read();
        if (opt == 3) break ;
        int xa, ya, xb, yb, val;
        if (opt == 1) {
            xa = read(); ya = read(); val = read();
            Addq(xa, ya, 1, 0, val);
        } else {
            xa = read(); ya = read();
            xb = read(); yb = read();
            Addq(xa - 1, ya - 1, 2, (++ acnt), 1);
            Addq(xa - 1, yb, 2, acnt, -1);
            Addq(xb, ya - 1, 2, acnt, -1);
            Addq(xb, yb, 2, acnt, 1);
        }
    }
    Cdq(1, qcnt);
    For (i, 1, acnt) printf ("%d\n", ans[i]);
    return 0;
}

?

BZOJ2683: 簡單題(CDQ分治 + 樹狀數組)