你有一個\(N*N\)的棋盤,每一個格子內有一個整數,初始時的時候所有爲\(0\),如今須要維護兩種操做:c++
命令 | 參數限制 | 內容 |
---|---|---|
\(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\)。git
這個題是 cdq分治 的裸題吧。算法
一維:時間(按輸入順序就好了)數組
二維:\(x\)座標(cdq分治)數據結構
三維:\(y\)座標(樹狀數組)spa
這個題比較裸,可是cdq分治細節仍是有一點的(調的錯誤我能夠列一版了。。)code
但我想簡單講一下cdq分治(由於網上不少都很坑沒講清楚)排序
cdq是專門解決多維偏序的問題,好比像這一道題統計二維矩形的權值,或者直接求高維偏序的個數。遞歸
若是不用cdq分治,就只能樹套樹或者KD-tree這種巨型工業數據結構。並且樹套樹經常空間和常數都很恐怖,而且很難寫……內存
cdq分治是個比較好寫的東西,但其中的思想十分的巧妙和神奇。
你應該學過歸併排序求逆序對吧,那是最裸的cdq了。他就是利用了左邊的答案來更新右邊的答案,cdq就是在這個方面不一樣於普通的分治。
它每次算答案,只能在右邊區間算也就是\([mid+1,r]\)。這是爲何能夠這樣呢,由於你初始給它的序列,按這樣算的話,絕對只會算它原序列左邊的貢獻,不會算到右邊去。(想想,爲何) 這個只須要本身模擬下分治的區間劃分和左右區間考慮就好了。
這就能夠會強制使你一開始的那一維有序,對答案計算是正確的。(但切記最後給你的序列不必定是按你給它的順序了!!!)
而後它中間會有一個排序比較的過程,這就可使第二個維度變得有序了。(最後的序列必定是第二維度有序的) 而後根據前兩個維度算答案就好了,後面的維度全都是附加在這兩個維度上面的。
總的步驟:
這道題,就是對於全部操做進行cdq分治(通常都是對於操做進行分治)。
第三維用樹狀數組統計\(y\)的前綴和就好了,由於\(x\)已經排好序了,因此能夠直接算了。
左區間只執行Add
操做,右區間只執行Sum
操做。
對於一個詢問操做,要將它拆成4個詢問操做(就是相似詢問二維前綴和),加加減減就好了。
注意幾個細節(我調了好久的點)
val
而是y
;而後多拍幾組,寫個暴力很容易查出來的。
#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; }