6 条题解
-
1
热知识:scy 的题解中注释打错位置了,以下是正确的注释:
#include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/tree_policy.hpp> #define ll long long using namespace std; using namespace __gnu_pbds; // 定义 ordered_set,底层为红黑树,不允许重复元素 typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> ordered_set; int main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int n,q; cin>>n>>q; ordered_set s; for(int i=1,x;i<=n;i++)cin>>x,s.insert(x); while(q--) { int t,x;cin>>t>>x; if(t==0)s.insert(x); else if(t==1)s.erase(x); else if(t==2) { if(x>(int)s.size()) cout<< -1 <<'\n'; else cout<< *s.find_by_order(x-1) << '\n'; //find_by_order是0_based,也就是排序后第一个数的下标为0,所以要-1 } else if(t==3) { int ans=(int)(s.order_of_key(x+1)); cout<<ans<<endl; // order_of_key(x) 返回严格小于 x 的个数,所以 <= x 的个数是 order_of_key(x + 1) } else if(t==4) { auto it=s.upper_bound(x); if(it==s.begin())cout<<-1<<'\n'; else cout<< *(--it)<<'\n'; } else if(t==5) { auto it=s.lower_bound(x); if(it==s.end())cout<<-1<<'\n'; else cout<< *it <<'\n'; } } return 0; } -
0
在 C++ 中,标准库的
std::set虽然支持插入、删除以及通过lower_bound/upper_bound查询前驱和后继,但不支持 复杂度的“查询第 小”和“查询小于等于 的个数”。如果强行使用std::set遍历来实现操作 2 和 3,时间复杂度会退化为 ,导致超时。解决方案: 在 C++ 竞赛中,解决这类“有序集合”问题的标准做法是使用 GNU PBDS(Policy-Based Data Structures)库中的
tree。它通常被封装为ordered_set,底层基于红黑树,接口与std::set几乎完全相同,但额外提供了两个强大的函数:find_by_order(k):返回第 小的元素(0-based)。order_of_key(x):返回严格小于 的元素个数。
ordered_set解法:必背
#include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/tree_policy.hpp> #define int long long using namespace std; using namespace __gnu_pbds; // 定义 ordered_set,底层为红黑树,不允许重复元素 typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> ordered_set; signed main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int n,q; cin>>n>>q; ordered_set s; for(int i=1,x;i<=n;i++)cin>>x,s.insert(x); while(q--) { int t,x;cin>>t>>x; if(t==0)s.insert(x); else if(t==1)s.erase(x); else if(t==2) { if(x>(int)s.size()) cout<< -1 <<'\n'; else cout<< *s.find_by_order(x-1) << '\n';// order_of_key(x) 返回严格小于 x 的个数,所以 <= x 的个数是 order_of_key(x + 1) } else if(t==4) { auto it=s.upper_bound(x); if(it==s.begin())cout<<-1<<'\n'; else cout<< *(--it)<<'\n'; } else if(t==5) { auto it=s.lower_bound(x); if(it==s.end())cout<<-1<<'\n'; else cout<< *it <<'\n'; } } return 0; }细节说明:
- 头文件:需要额外包含
<ext/pb_ds/assoc_container.hpp>和<ext/pb_ds/tree_policy.hpp>,并引入__gnu_pbds命名空间。 - 去重特性:
null_type和less<int>的组合使得该tree表现为一个不允许重复元素的集合(即set)。如果插入已存在的元素或擦除不存在的元素,它会自动忽略,完美契合题目要求。 - 前驱与后继:
- 操作 4(
<= x的最大值):使用upper_bound(x)找到第一个> x的元素,它的前一个元素即为所求。如果它指向begin(),说明没有<= x的元素。 - 操作 5(
>= x的最小值):使用lower_bound(x)找到第一个>= x的元素。如果它指向end(),说明没有>= x的元素。
- 操作 4(
- 兼容性:该解法依赖 GCC 编译器的 PBDS 库,在绝大多数 OI/ACM 评测机(如洛谷、Codeforces、AtCoder 等)上均可直接编译运行。
用set超时52分程序:
#include <bits/stdc++.h> #define int long long using namespace std; signed main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int n, q; cin >> n >> q; set<int> s; for (int i = 0; i < n; i++) { int x; cin >> x; s.insert(x); } for (int i = 0; i < q; i++) { int t, x; cin >> t >> x; if (t == 0) { // 插入 x(set自动去重) s.insert(x); } else if (t == 1) { // 删除 x(若不存在则不操作) s.erase(x); } else if (t == 2) { // 查询第 x 小的元素 (1-based) if (x > (int)s.size()) { cout << -1 << '\n'; } else { auto it = s.begin(); advance(it, x - 1); // 注意:此处复杂度为 O(N) cout << *it << '\n'; } } else if (t == 3) { // 查询 <= x 的元素个数 auto it = s.upper_bound(x); cout << distance(s.begin(), it) << '\n'; // 注意:此处复杂度为 O(N) } else if (t == 4) { // 查询 <= x 的最大元素 (前驱) auto it = s.upper_bound(x); if (it == s.begin()) { cout << -1 << '\n'; } else { it--; cout << *it << '\n'; } } else if (t == 5) { // 查询 >= x 的最小元素 (后继) auto it = s.lower_bound(x); if (it == s.end()) { cout << -1 << '\n'; } else { cout << *it << '\n'; } } } return 0; }细节说明与复杂度警告:
- 自动去重与删除:
std::set天然保证元素唯一,因此操作 0 直接insert即可;操作 1 直接erase(x),如果x不存在,set会自动忽略,不会报错。 - 操作 4 和 5(前驱与后继):使用
upper_bound和lower_bound可以在 时间内完成,这是set的强项。 - 操作 2 和 3 的 复杂度警告:
std::set底层是红黑树,其迭代器是双向迭代器,不支持随机访问。- 因此,
std::advance(it, k)只能一步步向后移动,std::distance(first, last)也只能一步步向前计数。这两者的时间复杂度均为 。 - 在 的数据规模下,如果多次执行操作 2 或 3,必然会导致超时(TLE)。
补充建议:如果在实际比赛中遇到此题且必须使用纯 STL(不使用 PBDS),通常的解法是结合树状数组/线段树与离散化,或者使用你之前熟悉的分块思想(将值域分块)来维护,从而将操作 2 和 3 的复杂度降至 或 。单纯依赖
std::set无法在底层结构上突破 的限制。 -
0
用平衡树写了篇题解
#include<bits/stdc++.h> using namespace std; #define lc(p) tr[p].ls #define rc(p) tr[p].rs const int M=1e6+10;//注意此处M为N+Q map<int,bool>vv;//vv记录是否在S中 struct node { int ls,rs,siz,val,rnd; }tr[M]; int trlen,rt; int newd(int v) { tr[++trlen]={0,0,1,v,rand()}; return trlen; } void pushup(int p) { tr[p].siz=tr[lc(p)].siz+tr[rc(p)].siz+1; } void split(int p,int v,int &x,int &y) { if(!p) { x=y=0; return ; } if(tr[p].val<=v) { x=p; split(rc(p),v,rc(x),y); } else { y=p; split(lc(p),v,x,lc(y)); } pushup(p); } int merge(int x,int y) { if(!x||!y) { return x+y; } if(tr[x].rnd<tr[y].rnd) { rc(x)=merge(rc(x),y); pushup(x); return x; } else { lc(y)=merge(x,lc(y)); pushup(y); return y; } } void ins(int v) { int x,y; split(rt,v-1,x,y); rt=merge(merge(x,newd(v)),y); } void del(int v) { int x,y,z; split(rt,v-1,x,y); split(y,v,y,z); rt=merge(merge(x,merge(lc(y),rc(y))),z); } int getrnd(int v) { int x,y; split(rt,v-1,x,y); int res=tr[x].siz+vv[v]; rt=merge(x,y); return res; } int getval(int p,int k) { if(tr[lc(p)].siz+1==k) { return tr[p].val; } if(k<=tr[lc(p)].siz) { return getval(lc(p),k); } else { return getval(rc(p),k-tr[lc(p)].siz-1); } } int getpre(int v) { int x,y; split(rt,v,x,y); int r=tr[x].siz; int res=getval(x,r); rt=merge(x,y); return res; } int getnxt(int v) { int x,y; split(rt,v-1,x,y); int res=getval(y,1); rt=merge(x,y); return res; }//前面为平衡树板子 int main() { int n,m; scanf("%d%d",&n,&m); int sum=n;//sum为当前S的总数 for(int i=1;i<=n;i++) { int x; scanf("%d",&x); ins(x); vv[x]=1; } while(m--) { int op,x; scanf("%d%d",&op,&x); op++; if(op==1) { if(vv[x]) { continue; } ins(x); vv[x]=1; sum++; } else if(op==2) { if(!vv[x]) { continue; } del(x); vv[x]=0; sum--; } else if(op==3) { if(sum<x) { printf("-1\n"); continue; } printf("%d\n",getval(rt,x)); } else if(op==4) { if(!sum) { printf("0\n"); continue; } printf("%d\n",getrnd(x)); } else if(op==5) { if(!sum||x<getval(rt,1))//S中最小的数 { printf("-1\n"); continue; } printf("%d\n",getpre(x)); } else { if(!sum||x>getval(rt,sum))//S中最大的数 { printf("-1\n"); continue; } printf("%d\n",getnxt(x)); } } return 0; } -
0
pb_ds解法(目前最优解):#include<bits/stdc++.h> #include<bits/extc++.h> using namespace std; using namespace __gnu_pbds; tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>s; int main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int n,q;cin>>n>>q; for(int i=1,x;i<=n;i++)cin>>x,s.insert(x); while(q--) { int op,x;cin>>op>>x; if(op==0)s.insert(x); if(op==1)s.erase(x); if(op==2) { auto it=s.find_by_order(x-1); if(it==s.end())cout<<-1<<'\n'; else cout<<*it<<'\n'; } if(op==3) { auto it=s.upper_bound(x); if(it==s.end())cout<<s.size()<<'\n'; else { int num=*it,id=s.order_of_key(num); cout<<id<<'\n'; } } if(op==4) { auto it=s.upper_bound(x); if(it==s.begin())cout<<-1<<'\n'; else it--,cout<<*it<<'\n'; } if(op==5) { auto it=s.lower_bound(x); if(it==s.end())cout<<-1<<'\n'; else cout<<*it<<'\n'; } } return 0; } -
0
#include<bits/stdc++.h> using namespace std; #define int long long #define N 100000 #define pb push_back set<int>se[N]; int n,q; int B,nB; int sum; vector<int>a,lsh; vector<pair<int,int> >query; unordered_map<int,int>mp; int getid(int x){ return (int)(upper_bound(lsh.begin(),lsh.end(),x)-lsh.begin()-1); } void ins(int x){ x=getid(x); int k=x/B; auto &s=se[k]; if(!s.count(x)) s.insert(x),sum++; } void era(int x){ int xx=getid(x); if(lsh[xx]==x)x=xx; else return; int k=x/B; auto &s=se[k]; if(s.count(x)) s.erase(x),sum--; } int kth(int k){ if(k<1||k>sum)return -1; for(int i=0;i<nB;i++){ int siz=se[i].size(); if(k>siz)k-=siz; else{ auto it=se[i].begin(); advance(it,k-1); return *it; } } return -1; } int lessnum(int x){ x=getid(x); int k=x/B; auto &s=se[k]; int cnt=0; for(int i=0;i<k;i++)cnt+=se[i].size(); auto it=s.upper_bound(x); cnt+=distance(s.begin(),it); return cnt; } int smaller(int x){ x=getid(x); int k=x/B; auto &s=se[k]; auto it=s.upper_bound(x); if(it!=s.begin()){ return *--it; } for(int i=k-1;i>=0;i--){ if(se[i].size())return *se[i].rbegin(); } return -1; } int larger(int x){ x=lower_bound(lsh.begin(),lsh.end(),x)-lsh.begin(); if(x==lsh.size())return -1; int k=x/B; auto &s=se[k]; auto it=s.lower_bound(x); if(it!=s.end()){ return *it; } for(int i=k+1;i<nB;i++){ if(se[i].size())return *se[i].begin(); } return -1; } void init(){ sum=0; for(int x:a){ x=getid(x); int k=x/B; auto &s=se[k]; if(!s.count(x)){ s.insert(x);sum++; } } for(int x:lsh){ mp[getid(x)]=x; } mp[-1]=-1; } signed main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin>>n>>q; for(int i=1;i<=n;i++){ int x;cin>>x; a.pb(x);lsh.pb(x); } for(int i=1;i<=q;i++){ int op,x;cin>>op>>x; query.pb({op,x}); if(op==0)lsh.pb(x); } sort(lsh.begin(),lsh.end()); lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end()); n=lsh.size(); B=500; nB=(n+B-1)/B; init(); if(n==0){ for(auto i:query){ int op=i.first,x=i.second; switch(op){ case 2: cout<<"-1\n"; break; case 3: cout<<"0\n"; break; case 4: cout<<"-1\n"; break; case 5: cout<<"-1\n"; break; } } return 0; } for(auto i:query){ int op=i.first,x=i.second; switch(op){ case 0: ins(x); break; case 1: era(x); break; case 2: cout<<mp[kth(x)]<<'\n'; break; case 3: cout<<lessnum(x)<<'\n'; break; case 4: cout<<mp[smaller(x)]<<'\n'; break; case 5: cout<<mp[larger(x)]<<'\n'; break; } } return 0; } -
0
#include<bits/stdc++.h> #define lc(p) tr[p].ls #define rc(p) tr[p].rs using namespace std; typedef long long ll; int id,rt; struct N{ int ls,rs,v,rd,sz; }tr[10000010]; mt19937 rd(999983); int nd(int v){ tr[++id]={0,0,v,rd(),1}; return id; } void pushup(int p){ tr[p].sz=tr[lc(p)].sz+tr[rc(p)].sz+1; } void split(int p,int v,int &x,int &y){ if(!p){ x=y=0; return ; } if(tr[p].v<=v){ x=p; split(rc(p),v,rc(p),y); } else{ y=p; split(lc(p),v,x,lc(p)); } pushup(p); } int merge(int x,int y){ if(!x||!y)return x+y; if(tr[x].rd<tr[y].rd){ rc(x)=merge(rc(x),y); pushup(x); return x; } else{ lc(y)=merge(x,lc(y)); pushup(y); return y; } } void ins(int v){ int x,y,z; split(rt,v-1,x,y); split(y,v,z,y); rt=merge(merge(x,nd(v)),y); } void del(int v){ int x,y,z; split(rt,v,x,y); split(x,v-1,x,z); // z=merge(lc(z),rc(z)); rt=merge(x,y); } int getval(int p,int k){ while(1){ if(tr[lc(p)].sz+1==k)return tr[p].v; if(tr[lc(p)].sz>=k)p=lc(p); else k-=tr[lc(p)].sz+1,p=rc(p); } } int getrk(int v){ int x,y; split(rt,v,x,y); int ans=tr[x].sz; rt=merge(x,y); return ans; } int getpre(int v){ int x,y; split(rt,v,x,y); int ans=getval(x,tr[x].sz); rt=merge(x,y); return ans; } int getnxt(int v){ int x,y; split(rt,v-1,x,y); int ans=getval(y,1); rt=merge(x,y); return ans; } int main(){ ios::sync_with_stdio(0); cin.tie(0); int n,q; cin>>n>>q; ins(-1);ins(1e9); for(int i=1,x;i<=n;i++){ cin>>x; ins(x); } for(int i=1;i<=q;i++){ int op,x; cin>>op>>x; if(op==0){ ins(x); } else if(op==1){ del(x); } else if(op==2){ if(x>tr[rt].sz-2)cout<<"-1\n"; else cout<<getval(rt,x+1)<<'\n'; } else if(op==3){ cout<<getrk(x)-1<<'\n'; } else if(op==4){ int ans=getpre(x); if(ans==-1)cout<<"-1\n"; else cout<<ans<<'\n'; } else if(op==5){ int ans=getnxt(x); if(ans==1e9)cout<<"-1\n"; else cout<<ans<<'\n'; } } return 0; }
- 1
信息
- ID
- 8118
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 9
- 标签
- 递交数
- 249
- 已通过
- 18
- 上传者