set
底层实现是用红黑树。
set 建立
set<int> s; // 不可重,默认升序
set<int,less> s; // 不可重,升序
set<int,greater> s; // 不可重,降序
multiset<int> s; // 可重集
set 也可以重载,利用结构体实现。
重载方式同 priority_queue 。
set 插入及访问
set<int>::iterator it;
s.insert(x); // 插入元素 x
s.begin(); // 最前面的迭代器
s.end(); // 最后一个元素之后的迭代器(实则空)
s.rbegin(); // 最后一个迭代器
s.rend(); // 最前面的前一个迭代器
pair<set<int>::iterator,bool> it=s.insert(x);
if(it.second) { 插入成功 }
else { 插入失败 }
set 大小
s.size(); //返回容器中元素的数目
s.empty(); //判断容器是否为空
set 的删除操作
s.clear(); //清除所有元素
s.erase(it); //删除 it 迭代器所指的元素,返回下一个元素的迭代器。
s.erase(l,r); //删除区间 [l,r) 的所有元素,返回下一个元素的迭代器。
s.erase(x); //删除容器中值为 x 的元素。
有的时候为了避免删除一个空的位置,在删除是可以采用以下操作:
s.erase(s.find(*it));
set 的查找操作
s.find(x); //查找 x 元素,返回指向 x 元素的迭代器。
s.count(x); //返回容器中值为 x 的元素个数。对 set 来说,要么是 0,要么是 1。对 multiset 来说,值可能大于 1。
s.lower_bound(x); //返回第一个 >=x 元素的迭代器
s.upper_bound(x); // 返回第一个 >x 元素的迭代器。
s.equal_range(x); //返回容器中与 x 相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如 [l,r) 。
例题
P1081 [NOIP2012 提高组] 开车旅行
用 \(\text{set}\) 维护的部分:
给定数列 \(\{h\}\) (\(h_i\) 互不相同),定义两点 \(i,j(i<j)\) ,它们间的距离 \(dist(i,j)\) 为 \(abs(h[i]-h[j])\) 。
对于每个 \(i\) ,求出距离 \(i\) 最近、次近的 \(j(j>i)\) (若距离一致,\(h\) 越小的 \(j\) 距离 \(i\) 更近)。
考虑用 set 与 lower_bound 实现。
由于最后的几个数找不到相应的答案,因此要在 set 中提前加入极大、极小的值。
每次查询 \(i\) 时,查出比 \(h[i]\) 小的最大编号,再访问其向前、向后的迭代器即可。
部分代码
h[0]=inf,h[n+1]=-inf;
st.insert((Data){inf,0}),st.insert((Data){inf,0});
st.insert((Data){-inf,n+1}),st.insert((Data){inf,n+1});
for(int i=n;i;i--)
{
int ga,gb; // ga:max_max gb:max_min
st.insert((Data){h[i],i});
set<Data>::iterator it=st.lower_bound((Data){h[i],i});
it--;
int ln=(*it).num,lh=(*it).val;
it++,it++;
int rn=(*it).num,rh=(*it).val;
it--;
if(abs(lh-h[i])<=abs(rh-h[i]))
{
gb=ln,it--,it--;
if(abs(h[i]-(*it).val)<=abs(rh-h[i])) ga=(*it).num;
else ga=rn;
}
else
{
gb=rn,it++,it++;
if(abs(h[i]-(*it).val)>=abs(lh-h[i])) ga=ln;
else ga=(*it).num;
}
fa[0][i][0]=ga;
fa[0][i][1]=gb;
}
priority_queue
应用——对顶堆
维护
void tosame(int size_l)
{
while(qmin.size()<siz_l) qmin.push(qmax.top()),qmax.pop();
while(qmin.size()>siz_l) qmax.push(qmin.top()),qmin.pop();
}
void check()
{
while(qmin.top()>qmax.top())
{
qmin.pop(),qmin.push(y);
qmax.pop(),qmax.push(x);
}
}
例题
给出 \(2n\) 个点,我们需要挑 \(1\) 个点,使得这 \(2n\) 个点到该点的距离和最小。
先考虑 \(k=1\) 的情况,这其实是一个很经典的结论,最优位置显然在中位数处(即排序后第 \(N\) 个点和第 \(N+1\) 个点之间的任意一点)取得。
接下来是 \(K=2\) 的情况。此时集合点变成了两个,画图后会发现,对于一条线段 \(AB\) 而言,选择离这个线段中点较近的集合点结果最优。
考虑将所有线段按 \(s_i+t_i\) 的顺序排序,枚举区域分界点,则分界点左边的区域前往左侧集合点,右边的区域前往右侧集合点,问题变成了 \(K=1\) 的情况。
设集合大小为 \(s\),我们维护一个大根堆,存放前 \(\dfrac{s}{2}\) 小的元素,再维护一个小根堆,存放后 \(\dfrac{s}{2}\) 小的元素,则中位数为两堆的堆顶(任取其一即可)。
include
reverse 翻转
翻转一个 vector
:
reverse(a.begin(),a.end());
翻转一个数组:
reverse(a+1,a+n+1);
unique 去重
unique
用于“去除”容器中相邻的重复元素(将重复的放在容器末尾),并返回去重后的尾地址。
由于去除的是相邻的元素,一般将容器排好序后去重。
例如:
int a[10]={1,1,2,2,2,3,3,4,5,5};
int ans=unique(a,a+10)-a;
则 \(ans\) 的值为 \(5\) 。
vector
去重同理:
int m=unique(a.begin(),a.end())-a.begin();
rand_shuffle 随机打乱
用法同 reverse ,经常在模拟退火与爬山算法中使用。
next_permutation 下一个排列
若存在下一个排列,则返回值为 true
,否则为 false
。
lower_bound/upper_bound 二分
指定部分应该是排好序的!
lower_bound
返回第一个大于等于 \(x\) 的元素的迭代器。
upper_bound
第一个大于 \(x\) 的元素的迭代器。
例如:
查找 int
数组中大于等于 \(x\) 的最小整数下标:
int i=lower_bound(a+1,a+n+1,x)-a;
在 vector
中查找小于等于 \(x\) 的最大整数(假设存在):
int i=*--upper_bound(a.begin(),a.end(),x);