【CDQ分治】 CF641E
阿新 • • 發佈:2022-03-31
題面。
- 題意 :
給定一個 帶 時間戳 的 可重集 (一開始為 空集), 每次進行以下幾種操作。
-
在時刻 \(t\) 加入一個數 \(x\)
-
在時刻 \(t\) 刪除一個數 \(x\)
-
在時刻 \(t\) 查詢一個數 \(x\) 的出現次數
- 思路 :
線上很棘手,考慮離線下來做。用一個狀態四元組 \((tim, b,c, d)\) 去表示一個操作。
分別表示為 第 \(tim\) 個操作,第 \(op\) 種操作, 第 \(b\) 號時間,元素為 \(c\), 對於元素的出現次數影響為 \(d\) (若為查詢則為 \(0\) ).
然後對於一個 第 \(tim\) 個操作為詢問 的答案就是 :
顯然,這變成了一個三維偏序問題,做 \(CDQ\) 分治即可。
可以考慮容斥, 也可以開個桶去解決,後者加個離散化和前者一樣。
- 程式碼 :
桶使用 \(map\) 來實現,總時間複雜度為 \(O(n\log^2n)\) 。
// Coding by Custlo // Tag : CDQ Method // Time : 2022 - 3 - 26 #include <algorithm> #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <map> using namespace std; const int N = 2e5 + 5; inline int read () { int ans = 0; char c = getchar(), last = ' '; while(c < '0' || c > '9') last = c,c = getchar(); while(c >= '0' && c <= '9') ans = (ans << 1) + (ans << 3) + c - '0',c = getchar(); return last == '-' ? - ans : ans; } map <int, int> exists; struct NODE { int a, b, c, op; } a[N]; // 狀態 inline bool cmp (NODE a, NODE b) { return a.a < b.a; } inline bool cmp2 (NODE a, NODE b) { return a.b < b.b; } int n, op[N], res[N]; inline void CdqMethod (int l, int r) { if (l == r) return ; int mid = (l + r) >> 1; CdqMethod(l, mid), CdqMethod(mid + 1, r); sort(a + l, a + r + 1, cmp2); for (int i = l; i <= r; i ++) { if (a[i].a <= mid ) { if (a[i].op != 3) exists[a[i].c] += (a[i].op == 1 ? 1 : -1); // 計算貢獻 } else if (a[i].op == 3) res[a[i].a] += exists[a[i].c]; // 求答案 } for (int i = l; i <= r; i ++) if (a[i].a <= mid && a[i].op != 3) exists[a[i].c] -= (a[i].op == 1 ? 1 : -1); // 撤銷貢獻 sort(a + l, a + r + 1, cmp); } // cdq 分治 int main () { n = read(); for (int i = 1; i <= n; i ++) { op[i] = a[i].op = read(), a[i].a = i, a[i].b = read(), a[i].c = read(); } CdqMethod(1, n); for (int i = 1; i <= n; i ++) if (op[i] == 3) printf("%d\n", res[i]); // 輸出答案 return 0; }