1. 程式人生 > 其它 >【CDQ分治】 CF641E

【CDQ分治】 CF641E

題面

  • 題意 :

給定一個 帶 時間戳可重集 (一開始為 空集), 每次進行以下幾種操作。

  1. 在時刻 \(t\) 加入一個數 \(x\)

  2. 在時刻 \(t\) 刪除一個數 \(x\)

  3. 在時刻 \(t\) 查詢一個數 \(x\) 的出現次數

  • 思路 :

線上很棘手,考慮離線下來做。用一個狀態四元組 \((tim, b,c, d)\) 去表示一個操作。

分別表示為 第 \(tim\) 個操作,第 \(op\) 種操作, 第 \(b\) 號時間,元素為 \(c\), 對於元素的出現次數影響為 \(d\) (若為查詢則為 \(0\) ).

然後對於一個 第 \(tim\) 個操作為詢問 的答案就是 :

\[\sum_{i < tim} \sum_{a[i].b \leq b,a[i].c = c}a[i].d \]

顯然,這變成了一個三維偏序問題,做 \(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;
}