维护一个集合的mex,每次有三种操作: 其实如果只维护操作1,3是比较容易的,只需要每次记录数是否在集合中,并且在每次查询的时候记录下本次数k的答案(每次倍增的查询即可),以便下次查询的时候不必重复查询。 而现在因为有了 减 操作,所以导致了原先的答案有可能会被他影响。所以我们可以将这种影响记录下来用于查询经过减操作后的答案。 所以我们维护一个map<int,vector> change change[x] 表示的是当x受到减操作后,会影响到的数 当last[x]受到改变时,例如当我们删除了4时,我们便可以直接遍历change[4],说明2的答案在这个减操作后会被影响,此时查询2时的答案便是4(如果只删除了4) 此外我们还需要维护一个set,表示数的答案区间内的mex,通俗点讲就是k的倍数中哪些数当前不在集合内,哪些数在集合内 结合change以后,我们每次进行 '+' || '-' 操作时,维护一下del题目链接
题目大意
题目思路:
\(~~\)例如:当我们查询k = 2时,会经过2,4,6,8,10...,那就说明2的改变会影响这些数的值的改变:
ans[x]
2
4
6
8
10
...
change[last[x]]
2
2
2
2,4
2
...
map<int,set
当我们删除这个数时便insert,否则erase,我们维护的是数x的mex
每次查询时如果存在del.size()>0,说明答案就是*del[x].begin()
否则倍增查询答案
代码实现
# include<iostream>
# include<bits/stdc++.h>
using namespace std;
# define int long long
# define endl "\n"
const int N = 5e5 + 10;
void solve() {
int n;
cin >> n;
map<int, int> ans;
map<int, bool> vis;
map<int,vector<int> > change;
map<int,set<int>> del;
while(n--){
char op;
int x;
cin>>op>>x;
if(op == '+'){
vis[x] = 1;
for(auto y:change[x]){
del[y].erase(x);
}
}
else if(op == '-'){
vis[x] = 0;
for(auto y:change[x]){
del[y].insert(x);
}
}
else{
if(!ans[x]) ans[x] = x;
if(del[x].size()){
cout<<*del[x].begin()<<endl;
}
else{
while(vis[ans[x]]){
change[ans[x]].push_back(x);
ans[x] += x;
}
cout<<ans[x]<<endl;
}
}
}
}
int tt;
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
tt = 1;
// cin >> tt;
while (tt--)solve();
return 0;
}