Codeforces Round #830 (Div. 2)D2. Balance (Hard version)(数据结构)

题目链接


题目大意

维护一个集合的mex,每次有三种操作:

  1. '+' x:将数 x 插入集合中
  2. '-' x:将数 x 移除集合
  3. '?' k:询问满足mex的数是k的倍数 既集合中未出现的数中最小的数可以整除k

题目思路:

其实如果只维护操作1,3是比较容易的,只需要每次记录数是否在集合中,并且在每次查询的时候记录下本次数k的答案(每次倍增的查询即可),以便下次查询的时候不必重复查询。

而现在因为有了 操作,所以导致了原先的答案有可能会被他影响。所以我们可以将这种影响记录下来用于查询经过操作后的答案。

所以我们维护一个map<int,vector> change

change[x] 表示的是当x受到减操作后,会影响到的数
\(~~\)例如:当我们查询k = 2时,会经过2,4,6,8,10...,那就说明2的改变会影响这些数的值的改变:

ans[x] 2 4 6 8 10 ...
change[last[x]] 2 2 2 2,4 2 ...

当last[x]受到改变时,例如当我们删除了4时,我们便可以直接遍历change[4],说明2的答案在这个减操作后会被影响,此时查询2时的答案便是4(如果只删除了4)

此外我们还需要维护一个set,表示数的答案区间内的mex,通俗点讲就是k的倍数中哪些数当前不在集合内,哪些数在集合内
map<int,set> del
当我们删除这个数时便insert,否则erase,我们维护的是数x的mex

结合change以后,我们每次进行 '+' || '-' 操作时,维护一下del
每次查询时如果存在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;
}
posted @ 2022-10-24 13:08  empty_y  阅读(73)  评论(0)    收藏  举报