set实现常见平衡树操作

 

 

参考习题:LuoGu3369

【模板】普通平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 1. 插入 $x$ 数 2. 删除 $x$ 数(若有多个相同的数,因只删除一个) 3. 查询 $x$ 数的排名(排名定义为比当前数小的数的个数 $+1$ ) 4. 查询排名为 $x$ 的数 5. 求 $x$ 的前驱(前驱定义为小于 $x$,且最大的数) 6. 求 $x$ 的后继(后继定义为大于 $x$,且最小的数)

输入输出格式

输入格式

 

第一行为 $n$,表示操作的个数,下面 $n$ 行每行有两个数 $\text{opt}$ 和 $x$,$\text{opt}$ 表示操作的序号( $ 1 \leq \text{opt} \leq 6 $ )

输出格式

 

对于操作 $3,4,5,6$ 每行输出一个数,表示对应答案

输入输出样例

输入样例 #1

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

输出样例 #1

106465
84185
492737

说明

【数据范围】 对于 $100\%$ 的数据,$1\le n \le 10^5$,$|x| \le 10^7$ 来源:Tyvj1728 原名:普通平衡树 在此鸣谢

/

【结果】

 

 

#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#include<stdio.h>
#include<set>
using namespace std;
int n;
multiset<int>bbt;
multiset<int>::iterator it;
template<typename T>
inline void read(T &x){
    register bool f=0;register char ch=getchar();x=0;
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
    if(f) x=-x;
}
template<typename T,typename...Args>
inline void read(T &x,Args&...args){read(x);read(args...);}
int main(){
//    freopen("input.txt","r",stdin);
    read(n);
    for(int i=1,opt,k;i<=n;i++){
        read(opt,k);
        if(opt==1) bbt.insert(k);else
        if(opt==2) bbt.erase(bbt.lower_bound(k));else
        if(opt==3) printf("%d\n",distance(bbt.begin(),bbt.lower_bound(k))+1);else
        if(opt==4) it=bbt.begin(),advance(it,k-1),printf("%d\n",*it);else
        if(opt==5) printf("%d\n",*--bbt.lower_bound(k));else
        if(opt==6) printf("%d\n",*bbt.upper_bound(k));
    }
    return 0;
}

【分析】

 

优点:使用IO的快读优化&手动开启Ofast优化
缺陷:
if(opt==3) printf("%d\n",distance(bbt.begin(),bbt.lower_bound(k))+1);
if(opt==4) it=bbt.begin(),advance(it,k-1),printf("%d\n",*it);
时间消耗主要在以上两项操作distance、advance(封装于algorithm),时间复杂度均为O(n)

而pb_ds中的rb_tree在查询第k大和排名时封装了O(log n)的函数 参见这里
    
除此之外,set还有求交集、并集、差集的操作,时间复杂度都为O(m+n),m,n分别为两个容器的大小
事实上,任意有序容器皆可以(无序的可以先排序)
#include<bits/stdc++.h>
using namespace std;

int main()
{
    set<int> s1, s2, S;
    s1.insert(1);
    s1.insert(2);
    s1.insert(3);
    s2.insert(2);
    s2.insert(3);
    s2.insert(4);

    S.clear();//clear before operation
    set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), inserter(S, S.begin()));
    puts("S=");
    for(auto &v : S)
        printf("%d ", v);
    puts("");

    S.clear();
    set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), inserter(S, S.begin()));
    puts("S=");
    for(auto &v : S)
        printf("%d ", v);
    puts("");
    
    vector<int> res(10,0);
    vector<int>::iterator iter;
    iter=set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(),res.begin());
    res.resize(iter-res.begin()); 
    puts("S=");
    for(iter=res.begin();iter!=res.end();++iter){
        cout<<*iter<<endl;
    }
    return 0; 
}
交集并集差集 示例

 

注意!
不要尝试用 less_equal做pb_ds的rb_tree的参数来解决平衡树中多值重复的麻烦。
lower_bound与upper_bound都会有问题


原则上讲STL中不要用less_equal,有些算法可能会出问题,尤其是涉及“等值”和“等价”的概念,如set、map等(内置红黑树的结构)。

模板参数设为less_equal时,lower_bound永远不会返回和查找值一样的值,也就是说,find()函数永远不会返回end(),即查找失败。

STL中的 sort, merge等,可以用less_equal,但不代表所有地方都行。与其记住哪些可以、哪些不可以,不如都用less 好了。
sort以后,等值元素的顺序是不确定的,不同系统下给出的结果可能不同,除非使用同一版本的STL。

单就这个例子,
sort(vect.begin(), vect.end(), less<int>() );和
sort(vect.begin(), vect.end(), less_equal<int>() );
如果用less_equal,碰到2数相同时会多进行一次交换,浪费时间
 

posted @ 2020-04-24 09:57  神犇(shenben)  阅读(1103)  评论(0编辑  收藏  举报