Codeforces Round #830 (Div. 2)D2. Balance (Hard version)(数据结构)
题目链接
题目大意
维护一个集合的mex,每次有三种操作:
- '+' x:将数 x 插入集合中
- '-' x:将数 x 移除集合
- '?' 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
当我们删除这个数时便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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】