bitset
注意: 在竞技编程中,bitset 的大小必须是常量,不能用变量代替。
1 2 3 4 5 6 7 8 9 #include <bitset> #define int long long using namespace std;bitset<1000> bs; bitset<1000> bs2 (5 ) ;
1 2 3 4 5 6 7 bs.set (5 ); bs.reset (5 ); if (bs.test (3 )) { }bs.set (); bs.reset ();
1 2 3 4 5 6 7 bs1 |= bs2 bs1 &= bs2 bs1 ^= bs2 bs <<= k
1 2 3 4 5 6 bs.count () bs.any () bs.none ()
1 2 3 4 5 6 当需要遍历 bitset 里所有为 1 的位(比如找寻到达了哪些顶点)时,不要用普通 for 循环逐个 test (),使用底层优化的内置函数能快几十倍: bs._Find_first() bs._Find_next(i)
Vector
1 2 3 4 5 6 vector<int > v; vector<int > v (n) ; vector<int > v (n, x) ; vector<int > v = {1 ,2 ,3 }; vector<int > v2 (v) ; vector<int > v (arr, arr+n) ;
1 2 3 4 5 6 7 8 v.push_back (x); v.pop_back (); v.insert (v.begin ()+i, x); v.insert (v.begin ()+i, n, x); v.insert (v.begin ()+i, v2. begin (), v2. end ()); v.erase (v.begin ()+i); v.erase (v.begin ()+l, v.begin ()+r); v.clear ();
1 2 3 4 5 v[i]; v.at (i); v.front (); v.back (); v.data ();
1 2 3 4 5 6 7 v.size (); v.empty (); v.resize (n); v.resize (n, x); v.reserve (n); v.capacity (); v.shrink_to_fit ();
1 2 3 4 for (int i = 0 ; i < v.size (); i++) v[i];for (auto x : v) x;for (auto & x : v) x; for (auto it = v.begin (); it != v.end (); it++) *it;
常用算法(需 <algorithm>)
1 2 3 4 5 6 7 8 9 10 11 12 sort (v.begin (), v.end ()); sort (v.begin (), v.end (), greater <int >()); sort (v.begin (), v.end (), cmp); reverse (v.begin (), v.end ()); find (v.begin (), v.end (), x); count (v.begin (), v.end (), x); max_element (v.begin (), v.end ()); min_element (v.begin (), v.end ()); accumulate (v.begin (), v.end (), 0 ); unique (v.begin (), v.end ()); lower_bound (v.begin (), v.end (), x); upper_bound (v.begin (), v.end (), x);
Set
1 2 3 4 set<int > s; set<int > s = {1 ,2 ,3 }; set<int > s (v.begin(), v.end()) ; set<int , greater<int >> s;
1 2 3 4 5 s.insert (x); s.erase (x); s.erase (s.find (x)); s.erase (it1, it2); s.clear ();
1 2 3 4 5 6 s.find (x); s.count (x); s.contains (x); s.lower_bound (x); s.upper_bound (x); s.equal_range (x);
1 2 3 for (auto x : s) x;for (auto it = s.begin (); it != s.end (); it++) *it;for (auto it = s.rbegin (); it != s.rend (); it++) *it;
Multiset
与 set 基本相同,区别是允许重复元素
1 2 3 multiset<int > ms; multiset<int > ms = {1 ,1 ,2 ,3 }; multiset<int , greater<int >> ms;
1 2 3 4 5 ms.insert (x); ms.erase (x); ms.erase (ms.find (x)); ms.erase (it1, it2); ms.clear ();
1 2 3 4 5 6 ms.find (x); ms.count (x); ms.contains (x); ms.lower_bound (x); ms.upper_bound (x); ms.equal_range (x);
1 2 for (auto x : ms) x;for (auto it = ms.rbegin (); it != ms.rend (); it++) *it;
Priority Queue
1 2 3 4 5 6 priority_queue<int > pq; priority_queue<int , vector<int >, greater<int >> pq; priority_queue<int > pq (v.begin(), v.end()) ; auto cmp = [](int a, int b){ return a > b; };priority_queue<int , vector<int >, decltype (cmp)> pq (cmp);
1 2 3 pq.push (x); pq.pop (); pq.emplace (x);
1 2 3 4 5 6 7 8 9 while (!pq.empty ()) { cout << pq.top (); pq.pop (); } priority_queue<pair<int ,int >> pq;
iota
定义在 <numeric> 里,用来给区间填充连续递增的值 :
1 iota (begin, end, start_value);
1 2 3 4 5 6 7 8 9 10 vector<int > v (5 ) ;iota (v.begin (), v.end (), 0 ); iota (v.begin (), v.end (), 1 ); vector<int > idx (n) ;iota (idx.begin (), idx.end (), 0 );sort (idx.begin (), idx.end (), [&](int a, int b){ return arr[a] < arr[b]; });
常见 String API
1 2 3 4 string s; string s (n, 'a' ) ; string s = "hello" ; string s (s2, pos, len) ;
1 2 3 4 5 6 7 8 9 s.push_back ('c' ); s.pop_back (); s.append (s2); s += s2; s.insert (i, s2); s.insert (i, n, 'c' ); s.erase (i, len); s.replace (i, len, s2); s.clear ();
1 2 3 4 s[i]; s.at (i); s.front (); s.back ();
1 2 3 4 5 6 7 s.find (s2); s.find (s2, pos); s.rfind (s2); s.find_first_of ("abc" ); s.find_last_of ("abc" ); if (s.find (s2) != string::npos) { ... }
1 2 3 4 5 s.substr (pos); s.substr (pos, len); s.size (); s.empty (); s.resize (n);
1 2 3 4 to_string (123 ); stoi ("123" ); stoll ("123" ); stod ("1.23" );
1 2 3 4 toupper ('a' ); tolower ('A' ); for (auto & c : s) c = toupper (c);
1 2 3 4 5 6 isdigit (c); isalpha (c); isalnum (c); isspace (c); isupper (c); islower (c);
1 2 3 4 5 6 7 8 9 stringstream ss ("hello world foo" ) ;string token; while (ss >> token) { cout << token << "\n" ; }stringstream ss; ss << 123 ; string s = ss.str ();
getline
1 2 string s; getline (cin, s);
1 2 3 4 5 6 7 8 int n;cin >> n; getline (cin, s); cin >> n; cin.ignore (); getline (cin, s);
1 2 3 getline (cin, s, ',' ); getline (cin, s, ' ' );
1 2 3 4 string line; while (getline (cin, line)) { cout << line << "\n" ; }
1 2 3 4 5 6 string line = "1,2,3,4" ; stringstream ss (line) ;string token; while (getline (ss, token, ',' )) { cout << token << "\n" ; }
unique
O(N) 去重 (只去相邻重复)
离散化 (Sort + Unique + Erase) 必须先 Sort! m = unique(a, a+n) - a; 返回去重后末尾指针。
next_permutation O(N)
下一个全排列
暴力枚举全排列
生成字典序
do { ... } while(next_permutation(all(s)));
lower_boundO(logN)
找 ≥ \ge ≥ val 的第一个位置
LIS (最长上升子序列)
查找排名/前驱后继返回迭代器。
idx = lower_bound(all(v), val) - v.begin();
__gcd
O(logV)
最大公约数
数论、化简分数
lcm(a,b) = a/gcd(a,b)*b
fill
O(N)
赋值
初始化数组/容器
fill(a, a+n, val);比 memset 安全,支持任意值 (memset 只能 0/-1/0x3f)。
vector 我的最爱
支持可变大小,不支持开出来的大小清空
O(1) push_back(),pop_back()
O(n) insert(),erase()
连续空间,可以直接指针++
resize() 改变大小且填充默认值;reserve() 只预留空间不改大小(优化常数神器)。
邻接表存图 vector<int> G[N]
deque 支持双向
头尾插入放出O(1)push_front;push_back;pop_front;pop_back
适用于滑动窗口
0-1 BFS (边权只为0或1的最短路)
string
拼接 + 是 O(N)
配合 stringstream 做类型转换
s.find() 找不到返回 string::npos。
千万别在循环里 s = s + c,要用 s += c。
priority_queue 优先队列
push,pop,O(logn) 方便贪心最大最小
可以通过重载运算符来达成最大或者这最小
list
stack
queue
BFS
注意 队列不能遍历,没有operator[],如果要遍历却不出队可以使用deque
set 自动去重的有序集合
O(log n)查找 find();插入于删除
支持lower_bound(x)``upper_bound(x) 依旧O(log n)
场景:动态维护有序序列且快速去重;坐标压缩
清空数组set.clear()
注意:bc.push_back({t, (int)cnt.size()})(这里的cnt是set类型的)类似于这一句需要强制类型转换,因为set.size()函数返回的是无符号类型
multiset 和set完全一样但是允许重复元素
动态维护中位数 场景:滑动窗口中位数
贪心(类似堆,但支持删任意值)实现O(log n)美丽复杂度的查找与删除,利于维护优先队列(包括lower_bound(x)``upper_bound(x))
大坑: erase(val) 会删掉所有等于 val 的元素;erase(iter) 只删一个。 iter是迭代器
map
(例子定义:map<int,int> mp;)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 map<int , vector<pii>> mp; for (int i = 0 ; i < n; i++) { mp[fdk (aa[i])].push_back ({aa[i], i}); } vi ans (n + 1 ) ; for (auto &[k, hsh] : mp) { vi pp; for (int i = 0 ; i < hsh.size (); i++) { pp.push_back (hsh[i].first); } sort (all (pp)); for (int i = 0 ; i < hsh.size (); i++) { ans[hsh[i].second] = pp[i]; } }
std::unordered_set / unordered_map —— 哈希表(平均 O(1))但是好像最坏能到O(n)
我不会,也没做过类似的题,以下内容均由ai生成
平均时间复杂度O(1),find,insert,等操作都快
无序
不支持lower_bound
键值唯一
哈希冲突严重时退化成链表O(n)
字符串/大数 快速映射
频率统计
堆 std::priority_queue
好东西
最短路dijkstra
贪心
合并果子
写法:
在 priority_queue 的 cmp 中,return true 的含义是: “左边这个元素(a) 比 右边这个元素(b) 优先级更低。”
top O(1)
push/pop O(logN)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 struct Node { int val; int id; }; struct cmp { bool operator () (const Node& a, const Node& b) { return a.val > b.val; } }; int dist[10005 ]; struct cmp { bool operator () (int u, int v) { return dist[u] > dist[v]; } }; priority_queue<int , vector<int >, cmp> pq; int main () { priority_queue<Node, vector<Node>, cmp> pq; pq.push ({10 , 1 }); pq.push ({5 , 2 }); pq.push ({20 , 3 }); while (!pq.empty ()) { Node top = pq.top (); pq.pop (); cout << "Val: " << top.val << endl; } return 0 ; }
字典树模板
Trie 的优势在于“快速查找前缀”或者“大量字符串排序”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 struct Trie { int tre[MAXN][70 ]; int cnt[MAXN]; int idx = 0 ; void clear () { for (int i = 0 ; i <= idx; i++) { for (int j = 0 ; j < 65 ; j++) tre[i][j] = 0 ; cnt[i] = 0 ; } idx = 0 ; } int getnum (char ch) { if (ch >= 'A' && ch <= 'Z' ) { return ch - 'A' ; } else if (ch >= 'a' && ch <= 'z' ) { return ch - 'a' + 26 ; } else if (ch >= '0' && ch <= '9' ) { return ch - '0' + 52 ; } } void insert (string s) { int ptr1 = 0 ; int bra = 0 ; for (int i = 0 ; i < s.size (); i++) { if (!tre[ptr1][getnum (s[i])]) { idx++; tre[ptr1][getnum (s[i])] = idx; } ptr1 = tre[ptr1][getnum (s[i])]; cnt[ptr1]++; } } int query (string s) { int ptr2 = 0 ; for (int i = 0 ; i < s.size (); i++) { if (!tre[ptr2][getnum (s[i])]) { return 0 ; } ptr2 = tre[ptr2][getnum (s[i])]; } return cnt[ptr2]; } }myTrie; void solve () { myTrie.clear (); int n, q; cin >> n >> q; for (int i = 0 ; i < n; i++) { string s; cin >> s; myTrie.insert (s); } for (int i = 0 ; i < q; i++) { string t; cin >> t; cout << myTrie.query (t) << "\n" ; } }