CF 1732D2 Balance (Hard version)

题目描述

https://codeforces.com/contest/1732/problem/D2

简要题意:现在你有一个集合 $S$,$S$ 中只有一个元素 $0$,同时有 $q$ 次操作,操作有三种,第一种操作向集合中加入一个数 $x$;第二种操作从集合中删掉数 $x$;第三种操作给定 $k$,询问集合的 $k-mex$,$k-mex$ 表示最小的数 $x$,满足 $x$ 是 $k$ 的倍数,且在 $S$ 中不存在

$q\le 2\times 10^5,x,k\le 10^{18}$

Solution

我们首先考虑不存在删除操作如何做,容易发现我们对于每个 $k$ 维护答案即可,即我们记录最大的 $x$,满足 $0,k,\cdots,x-k$ 都存在且 $x$ 不存在,这个东西的具体操作就是在每次询问的时候暴力检查,注意到链的总长度大概是 $O(n\log )$ 的级别

然后我们考虑删除,对于每个 $k$ 我们依然记录最大的 $x$,不过我们记录的 $x$ 是历史最大,同时我们考虑对于每个 $k$ 记录 $k$ 到 $x$ 之间后来被删掉的数 $y$,这样我们每次询问的时候暴力判断每个被记录的 $y$ 是否被重新加入,如果全部被重新加入则从 $x$ 开始再次暴力判断

那么我们考虑当我们删掉一个数 $x$,我们如何找到 $x$ 作为 $y$ 的所有 $k$,这个东西其实非常简单,我们对于每个数 $x$ 开一个 $set$ 记录数 $x$ 所在的所有链的链尾,这样如果我们删掉 $x$,就在它所在的所有链尾将其作为 $y$ 加入,对于这个 $set$ 的维护,我们在询问暴力的时候维护即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <map>
#include <set>
#define maxn 200010
#define ll long long
using namespace std;

int q;

map<ll, ll> ans;
map<ll, set<ll>> S, T;
set<ll> ext;

inline void solve_1() {
ll x; cin >> x;
ext.insert(x);
}

inline void solve_2() {
ll x; cin >> x;
ext.erase(x);
for (auto y : S[x])
T[y].insert(x / y);
S[x].clear();
}

inline void solve_3() {
ll x; cin >> x;
if (!ans.count(x)) ans[x] = 1;
ll k = ans[x];
while (T[x].size()) {
ll k = *T[x].begin();
if (ext.count(k * x)) T[x].erase(k), S[k * x].insert(x);
else break;
}
if (T[x].size()) return cout << *T[x].begin() * x << "\n", void();
while (ext.count(k * x)) S[k * x].insert(x), ++k;
ans[x] = k; cout << k * x << "\n";
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> q;
while (q--) {
char c[10]; cin >> c;
if (c[0] == '+') solve_1();
else if (c[0] == '-') solve_2();
else solve_3();
}
return 0;
}