ACM-ICPC 中可能會使用到的庫
sort(v.first(),v.end(),cmp())
unique(v.first(),v.end(),cmp()) 第三個參數可以傳入一個bool型,用來判斷是不是相等,返回unique後的超尾
max_element(v.first(),v.end(),cmp()) 返回一個叠代器
max_element(v.first(),v.end(),cmp()) 返回一個叠代器
nth_elemtn(v.first(),v.first()+nth,v.end(),cmp()) 對整個容器部分排序後,返回nth叠代器。其中 $[first,nth)$ 都比nth小, $[nth,last)$ 都不小於nth。
reverse(v.first(),v.end())反轉
rotate(v.first(),v.middle(),v.end()) 左右交換位置,用處不大還慢(因為隨機訪問對CPU緩存機制的不友好,太差勁了)
random_shuffle(first, last, rand)產生隨機序列
pb_ds
//首先需要以下頭文件以及命名空間 #include <ext/pb_ds/tree_policy.hpp> #include <ext/pb_ds/assoc_container.hpp> using namespace __gnu_pbds;
優先隊列
可合並優先隊列pairing_heap_tag
此外,pb_ds庫的優先隊列支持合並操作,pairing_heap的合並時間復雜度是O(logn)的,可以說基本上完美代替了左偏樹。合並的調用方式是:
支持叠代器,可以記錄push的返回叠代器來對優先隊列中的元素進行修改
join(priority_queue &other) //合並兩個堆,other會被清空
split(Pred prd,priority_queue &other) //分離出兩個堆
因為重名的原因一定要加上 __gnu_pbds::
__gnu_pbds::priority_queue<int,greater<int>,pairing_heap_tag> pq;
// 對兩優先隊列進行一些操作
a.join(b);(O(1)合並)
此時優先隊列b內所有元素就被合並進優先隊列a中,且優先隊列b被清空。
名次樹/紅黑樹
#include <ext/pb_ds/tree_policy.hpp> #include <ext/pb_ds/assoc_container.hpp> #include <bits/stdc++.h> using namespace __gnu_pbds; using namespace std; typedef tree<int, null_type, less<int>, rb_tree_tag,tree_order_statistics_node_update> rbtree;
// int類型
// null_type為映射類型, 低版本g++為 null_mapped_type// less<int>, greater<int> 比較器// rb_tree_tag 和 splay_tree_tag 選擇樹的類型// tree_order_statistics_node_update 結點更新// insert, erase// order_of_key rank// find_by_order() kth// lower_bound() 前繼, >=x 最小的叠代器// upper_bound() 後繼 >x 最小的叠代器// a.join(b) b並入a,前提是兩顆樹的取值範圍不相交// a.split(v, b) key <= v的屬於a,其他屬於// 註意,插入的元素會去重,如set
叠代器支持++和--
計數從0開始而不是從1開始,例如查詢第k+1小的數,使用find_by_order()函數,返回的為叠代器。t.find_by_order(2),查詢(常說的第3小的數)
查詢比x小的數的個數,註意,是比x小的個數,不是x的排名。t1.order_of_key(2),查詢比2小的數的個數,相當於2的排名-1。
正常的splay
#include <ext/pb_ds/tree_policy.hpp>#include <ext/pb_ds/assoc_container.hpp>#include <bits/stdc++.h> using namespace __gnu_pbds; using namespace std; const int MAXN = 1000000 + 10; int price, menu[MAXN], cnt[MAXN]; template<class T>inline bool nextInt(T &n) { T x = 0, tmp = 1; char c = getchar(); while((c < ‘0‘ || c > ‘9‘) && c != ‘-‘ && c != EOF) c = getchar(); if(c == EOF) return false; if(c == ‘-‘) c = getchar(), tmp = -1; while(c >= ‘0‘ && c <= ‘9‘) x *= 10, x += (c - ‘0‘), c = getchar(); n = x*tmp; return true; } template<class T>inline void Out(T n) { if(n < 0) { putchar(‘-‘); n = -n; } int len = 0, data[20]; while(n) { data[len++] = n%10; n /= 10; } if(!len) data[len++] = 0; while(len--) putchar(data[len]+48); } struct Splay_Tree { struct Node { int father, childs[2], key, cnt, _size; inline void init() { father = childs[0] = childs[1] = key = cnt = _size = 0; } inline void init(int father, int lchild, int rchild, int key, int cnt, int sz) { this -> father = father, childs[0] = lchild, childs[1] = rchild; this -> key = key, this -> cnt = cnt, _size = sz; } } tre[MAXN]; int sign, root; inline void init() { sign = root = 0; } inline bool judge(int x) { return tre[ tre[x].father ].childs[1] == x; } inline void update(int x) { if(x) { tre[x]._size = tre[x].cnt; if(tre[x].childs[0]) { tre[x]._size += tre[ tre[x].childs[0] ]._size; } if(tre[x].childs[1]) { tre[x]._size += tre[ tre[x].childs[1] ]._size; } } } inline void rotate(int x) { int y = tre[x].father, z = tre[y].father, k = judge(x); //tre[y].childs[k] = tre[x].childs[!k], tre[ tre[x].childs[!k] ].father = y; //tre[x].childs[!k] = y, tre[y].father = x; //tre[z].childs[ tre[z].childs[1] == y ] = x, tre[x].father = z; if(k == 0) { ///zig tre[y].childs[0] = tre[x].childs[1], tre[ tre[x].childs[1] ].father = y; tre[x].childs[1] = y, tre[y].father = x; } else { ///zag tre[y].childs[1] = tre[x].childs[0], tre[ tre[x].childs[0] ].father = y; tre[x].childs[0] = y, tre[y].father = x; } tre[z].childs[ tre[z].childs[1] == y ] = x, tre[x].father = z; update(y); } inline void splay(int x,int goal) { for(int father; (father = tre[x].father) != goal; rotate(x) ) { if(tre[father].father != goal) { rotate(judge(x) == judge(father) ? father : x); } } root = x; } inline void insert_node(int x) { if(root == 0) { tre[++sign].init(0, 0, 0, x, 1, 1); root = sign; return ; } int now = root, father = 0; while(1) { if(tre[now].key == x) { tre[now].cnt ++; update(now), update(father); splay(now, 0); break; } father = now; if(x > tre[now].key) { now = tre[now].childs[1]; } else { now = tre[now].childs[0]; } if(now == 0) { tre[++sign].init(father, 0, 0, x, 1, 1); if(x > tre[father].key) { tre[father].childs[1] = sign; } else { tre[father].childs[0] = sign; } update(father); splay(sign, 0); break; } } } inline int pre() { int now = tre[root].childs[0]; while(tre[now].childs[1]) { now = tre[now].childs[1]; } return now; } inline int next() { int now = tre[root].childs[1]; while(tre[now].childs[0]) { now = tre[now].childs[0]; } return now; } inline int find_rank(int x) { /// 找x的排名 int now = root, ans = 0; while(1) { if(x < tre[now].key) { now = tre[now].childs[0]; } else { if(tre[now].childs[0]) { ans += tre[ tre[now].childs[0] ]._size; } if(x == tre[now].key) { splay(now, 0); return ans + 1; } ans += tre[now].cnt; now = tre[now].childs[1]; } } } inline int find_by_order(int x) { int now = root; while(1) { if(tre[now].childs[1] && x <= tre[ tre[now].childs[1] ]._size ) { now = tre[now].childs[1]; } else { int rchild = tre[now].childs[1], sum = tre[now].cnt; if(rchild) { sum += tre[rchild]._size; } if(x <= sum) { int ans = tre[now].key; splay(now, 0); return ans; } x -= sum; now = tre[now].childs[0]; } } } inline int find_rankx(int x) { /// 找排名為x的數字 int now = root; while(1) { if(tre[now].childs[0] && x <= tre[ tre[now].childs[0] ]._size ) { now = tre[now].childs[0]; } else { int lchild = tre[now].childs[0], sum = tre[now].cnt; if(lchild) { sum += tre[lchild]._size; } if(x <= sum) { return tre[now].key; } x -= sum; now = tre[now].childs[1]; } } } inline void del(int x) { find_rank(x); if(tre[root].cnt > 1) { tre[root].cnt --; update(root); return ; } if(!tre[root].childs[0] && !tre[root].childs[1]) { tre[root].init(); root = 0; return ; } if(!tre[root].childs[0]) { int old_root = root; root = tre[root].childs[1], tre[root].father = 0, tre[old_root].init(); return ; } if(!tre[root].childs[1]) { int old_root = root; root = tre[root].childs[0], tre[root].father = 0, tre[old_root].init(); return ; } int pre_node = pre(), old_root = root; splay(pre_node, 0); tre[root].childs[1] = tre[old_root].childs[1]; tre[ tre[old_root].childs[1] ].father = root; tre[old_root].init(); update(root); } inline bool find(int x) { int now = root; while(1) { if(now == 0) { return 0; } if(x == tre[now].key) { splay(now, 0); return 1; } if(x > tre[now].key) { now = tre[now].childs[1]; } else { now = tre[now].childs[0]; } } } } tre; int n, opt, x; int main() { scanf("%d", &price); int id = 1; while(nextInt(opt) && opt) { //scanf("%d", &x); nextInt(x); if(opt == 1) { /// add price of x tre.insert_node(x); cnt[x]++; /// 價格x的菜的數量 menu[id++] = x; /// 第id道菜價格x continue; } if(opt == 2) { int p = menu[x]; tre.find(p); if(cnt[p]) { cnt[p]--; } continue; } if(opt == 3) { int res = tre.find_by_order(x); if(res > price) { puts("Dui bu qi,Mei you."); } else if(cnt[res] == 0) { puts("Mei you. Zhe ge ke yi you. Zhe ge zhen mei you!"); } else { printf("You. %d Yuan.\n", res); } } } return 0; }
View Code
平衡樹
pb_ds庫這次內置了紅黑樹(red-black tree)、伸展樹(splay tree)和排序向量樹(ordered-vector tree,沒找到通用譯名,故自行翻譯)。這些封裝好的樹都支持插入(insert)、刪除(erase)、求kth(find_by_order)、求rank(order_of_key)操作
封裝好的紅黑樹可以達到手寫Treap的速度。
求kth(find_by_order)返回的是叠代器,求rank返回的是值,兩者都是從0開始計算的。
此外,它們也支持合並(join)和分離(split)操作。用法如下。
tree<int,null_type> a,b;
// 對兩平衡二叉樹進行一些操作
a.join(b);
// 對兩平衡二叉樹進行一些操作
a.split(v,b);
這裏需要進行一些解釋。join操作的前提是兩棵樹的key的取值範圍不相交,否則會拋出一個異常;合並後平衡二叉樹b被清空。split操作中,v是一個與key類型相同的值,表示key小於等於v的元素屬於平衡二叉樹a,其余的屬於平衡二叉樹b,註意此時後者已經存有的元素將被清空。
rope
#include<ext/rope> using namespace __gnu_cxx; append() string &append(const string &s,int pos,int n); //把字符串s中從pos開始的n個字符連接到當前字符串的結尾或 a.append(b); substr() s.substr(0,5); //獲得字符串s中從第零位開始長度為5的字符串(默認時長度為剛好開始位置到結尾) push_back(x);//在末尾添加x insert(pos,x);//在pos插入x,自然支持整個char數組的一次插入 erase(pos,x);//從pos開始刪除x個 copy(pos,len,x);//從pos開始到pos+len為止用x代替 replace(pos,x);//從pos開始換成x substr(pos,x);//提取pos開始x個 at(x)/[x];//訪問第x個元素
支持用數組下標訪問,不需要使用叠代器(叠代器會超時)
技巧:有時翻轉操作可以維護一個反方向的rope
聲明
rope<char> str;
哈希表
#include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/hash_policy.hpp> usingnamespace __gnu_pbds; cc_hash_table<string,int>mp1;//拉鏈法 gp_hash_table<string,int>mp2;//查探法(快一些)
不使用C++11的時候比map的效率大大提高
ACM-ICPC 中可能會使用到的庫