【CDQ分治】 CF641E
题面。
- 题意 :
给定一个 带 时间戳 的 可重集 (一开始为 空集), 每次进行以下几种操作。
-
在时刻 \(t\) 加入一个数 \(x\)
-
在时刻 \(t\) 删除一个数 \(x\)
-
在时刻 \(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;
}
登高自卑,行远自迩。

浙公网安备 33010602011771号