• C++STL(set……)


    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 提高组] 开车旅行

    ( ext{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);
    	 }
    }
    

    例题

    P3644 [APIO2015]八邻旁之桥

    给出 (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);
    

    bitset

    建立及运算符

    bitset<Maxlen> b[Maxn]; // 建立 Maxn 个长度为 Maxlen 的 bitset
    
    b[i][k];
    b[i]==b[j] / b[i]!=b[j] // 直接判断两个 bitset 是否相等
    & / &= / | / |= / ^ / ^= / ~
    << / >> / <<= / >>= // 整体移动
    

    成员函数

    b[i].count(); // 返回 1 的个数
    b[i].any(); // 如果存在 1 就返回 true,否则返回 false
    b[i].none(); // 如果所有位都是 0 就返回 true,否则返回 false
    b[i].all(); // 如果所有位都是 1 就返回 true,否则返回 false
    b[i].set(); // 将 b[i] 的每一位都设为 1
    b[i].reset(); // 将 b[i] 的每一位都设为 0
    b[i].flip(); // 翻转 b[i] 的每一位
    b[i]._Find_first(); // 返回第一个 true 的下标,若没有则返回 bitset 的大小
    b[i]._Find_next(pos); // 返回 >pos 的第一个 true 的下标,若没有色返回 bitset 的大小
    

    应用

    P4465 [国家集训队] JZPSTR

    你要对一个字符串进行三种操作:(记操作次数为 (m)

    • 在位置 (x_i) 处插入一个字符串 (y_i)

    • 删除位置 ([x_i, y_i)) 的字符串

    • 查询位置 ([x_i, y_i)) 的字符串包含多少次给定的子串 (z_i)

    (mle 10^4),插入总长度 (le 2 imes 10^6),任何时候字符串的长度 (le 10^6)

    $ exttt{solution}$

    这是 Claris 以前讲课的时候推荐的牛逼暴力题。

    可以用 bitset 实现这一题的所有操作,并吊打正解块状链表+后缀树!

    能够优化原因:这道题的字符集很小,可以对每一个字符维护,摆脱字符串的限制。

    一些必要操作:

    • 提取 (x) 为及以后的那些位 (b[i]>>x)<<x

    • b[1](x) 以后的维右移出 (y) 个空位:用原本的串异或上一题的高位,并赋上高位右移的结果。

    • 匹配一个字符串:考虑有一个 bitset 记录当前仍旧合法的位置的集合,开始时当然是所有合法的起始位置,即 ([x,y-len])。后面一位一位枚举匹配串,并用答案与上这一位和原串相同的位置。最后直接 count() 就好了。

    #define Maxn 1000005
    int m;
    bitset<Maxn> b[10],tmp;
    char s[Maxn];
    int main()
    {
    	 m=rd();
    	 for(int i=1,opt,x,y,len;i<=m;i++) // attention!下标从 0 开始 
    	 {
    	 	 opt=rd();
    	 	 if(!opt) // insert before x 
    	 	 {
    	 	 	 x=rd(),scanf("%s",s),len=strlen(s);
    	 	 	 for(int j=0;j<10;j++) tmp=(b[j]>>x)<<x,b[j]^=tmp^(tmp<<len);
    	 	 	 for(int j=0;j<len;j++) b[s[j]-'0'][x+j]=true;	
    		 }
    		 else if(opt==1) // delete [x,y-1]
    		 {
    		 	 x=rd(),y=rd();
    		 	 for(int j=0;j<10;j++) b[j]^=((b[j]>>x)<<x)^((b[j]>>y)<<x);
    		 }
    		 else // query times [x,y-1] z
    		 {
    		 	 x=rd(),y=rd(),scanf("%s",s),len=strlen(s);
    		 	 tmp.reset(),tmp=((~tmp)<<x)^((~tmp)<<(y-len+1));
    			 // 提取出只包含 [x,y-1] 的一段 1
    			 for(int j=0;j<len;j++) tmp=(j?(tmp<<1):tmp)&b[s[j]-'0'];
    			 printf("%d
    ",(int)tmp.count());
    		 }
    	 }
    	 return 0;
    }
    

    string 字符串

    查找

    string s,t;
    if((pos=s.find(t))!=(int)string::npos) { /* 在全局找到 t*/ }
    if((pos=s.find(t,x))!=(int)string::npos) { /* 在下标为 x 及以后找 t,注意下标从 0 开始*/ }
    

    注意在 string 在判断找不找得到的时候是用 string::npos,如果等于这个表示找不到,否则就返回下标。

    删除

    s.erase(x,len); // 从下标为 x 开始连续删除 t 位,并自动将后面的不过来
    

    平板电视 pb_ds

    可并堆

    #include<ext/pb_ds/priority_queue.hpp>
    __gun_pbds::__priority_queue<int> q;
    
    q1.join(q2); // 将 q2 合并到 q1,并将 q2 清空
    

    平衡树

    咕咕咕
    
  • 相关阅读:
    [React & Testing] Simulate Event testing
    [React & Testing] Snapshot testings
    [Node & Tests] Intergration tests for Authentication
    [CSS] Build a Fluid Loading Animation in CSS
    [Node & Testing] Intergration Testing with Node Express
    [Node] Stateful Session Management for login, logout and signup
    [Node.js] Serve Static Files with Express
    [JWT] JWT with HS256
    [Redux] Understand Redux Higher Order Reducers
    [Vue + TS] Create your own Decorators in Vue with TypeScript
  • 原文地址:https://www.cnblogs.com/EricQian/p/15207633.html
Copyright © 2020-2023  润新知