一、头文件与基础
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int,int>;
二、<algorithm> 算法全家桶(最常用)
1. sort ------ 排序
sort(a.begin(), a.end()); // 默认升序
sort(a.begin(), a.end(), greater<int>()); // 降序
// 自定义比较(lambda)
sort(v.begin(), v.end(), [](auto &x, auto &y) {
return x.val < y.val; // 小的在前
});
注意:
sort不稳定,相等元素相对顺序可能变- 需要稳定用
stable_sort - 结构体排序重载
<也可以,但 lambda 更灵活
2. min / max / clamp
int x = min(a, b), y = max(a, b);
int z = clamp(val, 0, 100); // C++17,val 限制在 [0,100]
不要 min(a++, b++),有未定义行为;用 min({a,b,c}) 三值比较要 C++11。
3. swap
swap(a, b);
自定义类型要有可移动/可拷贝,STL 容器 O(1) swap。
4. reverse / rotate
reverse(a.begin(), a.end()); // 翻转
rotate(a.begin(), a.begin() + k, a.end()); // [0,k) 移到末尾(左移 k)
// 右移 k:rotate(a.begin(), a.end()-k, a.end());
5. unique + erase(去重)
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end()); // 经典离散化前处理
unique 不去重,只是把不重复元素移到前面,返回新尾迭代器。
6. lower_bound / upper_bound(二分神器)
auto it = lower_bound(v.begin(), v.end(), x); // 第一个 >= x
auto it = upper_bound(v.begin(), v.end(), x); // 第一个 > x
int pos = lower_bound(v.begin(), v.end(), x) - v.begin();
| 函数 | 语义 |
|---|---|
lower_bound |
≥ x |
upper_bound |
> x |
binary_search |
存在性 bool |
必须有序,自定义比较时两个参数要一致:
lower_bound(v.begin(), v.end(), x, [](const Node& a, int b) { return a.val < b; });
三、数学函数
gcd / lcm(C++17)
int g = gcd(a, b); // 负数自动取 abs
int l = lcm(a, b); // 小心溢出
ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }
abs / labs / llabs
ll x = llabs(-1000000000000LL); // int abs 对 ll 会截断!
pow / sqrt
ll x = sqrt(n); // 浮点误差!整数开根要
ll x = (ll)sqrt(n); while(x*x>n)x--; while((x+1)*(x+1)<=n)x++;
永远不要对整数用 pow 做精确计算 ,pow(10,3) 可能是 999。
四、容器全家桶
vector
vector<int> v(n, 0); // n 个 0
v.push_back(x);
v.pop_back();
v.resize(n); // 改大小,多的截断,少补默认
v.reserve(n); // 只扩容量不初始化,不增加 size
v.emplace_back(x); // 比 push_back 少一次拷贝,快一丢丢
遍历中不能 push_back 然后依赖索引不失效 ,但迭代器会失效;resize 前访问越界直接 RE。
deque(双端队列)
deque<int> dq;
dq.push_back(x); dq.push_front(x);
dq.pop_front(); dq.pop_back();
滑动窗口队头队尾操作、单调队列常用
比 vector 慢,随机访问略慢,内存不连续
stack / queue / priority_queue
stack<int> st; st.push(x); st.top(); st.pop();
queue<int> q; q.push(x); q.front(); q.pop();
优先队列(大根堆默认)
priority_queue<int> pq; // 大根堆
priority_queue<int, vector<int>, greater<int>> minpq; // 小根堆
pq.push(x); pq.top(); pq.pop();
priority_queue 没有 clear() 方法 !想清空只能 pq = priority_queue<int>();
结构体小根堆写法:
struct Node { ll d; int u; };
priority_queue<Node, vector<Node>, function<bool(Node,Node)>> pq(
[](Node a, Node b) { return a.d > b.d; } // 注意:STL 里比较是"大于"表示小根
);
// 或者重载 operator< 时反过来写
set / multiset / map / multimap
set<int> s;
s.insert(x); s.erase(x); // 按值删所有(set 里只有一个)
s.erase(s.find(x)); // 按迭代器删 O(1) 不检查存在
if (s.count(x)) ...
auto it = s.lower_bound(x); // 第一个 >= x
找前驱/后继:
auto it = s.lower_bound(x);
if (it != s.begin()) it--; // 前驱
auto nxt = s.upper_bound(x); // 后继
erase(key) 返回删除个数,set 是 O(log n);erase(iterator) 是 O(1) amortized
multiset 删一个元素:
auto it = ms.find(x);
if (it != ms.end()) ms.erase(it); // 只删一个
ms.erase(x); // 全删光!
unordered_map / unordered_set
unordered_map<string, int> mp;
mp["abc"] = 1;
if (mp.count("abc")) ...
期望 O(1)
注意:
- 最坏 O(n)(卡哈希,CF 有专门卡数据)
- 自定义 struct 要写 hash 函数
- 不能存指针/迭代器依赖顺序
能用 map 就别用 unordered_map,除非 n 很大且不怕被卡
bitset(位运算神器)
bitset<1005> a, b;
a.set(); a.reset(); a.flip();
a[i] = 1;
cout << (a & b).count(); // 1 的个数
cout << a.to_string();
状态压缩 DP、BFS 判重、Floyd 传递闭包
大小必须是编译期常量 ,不能 bitset<n> 变量
array(C++11 定长数组)
array<int, 1005> a;
sort(a.begin(), a.end()); // 比 int a[1005] 多了 STL 接口
五、字符串
string s;
s.substr(pos, len); // len 超了就截到末尾
s.resize(n, '0');
s.find("abc"); // 找不到返回 string::npos
s += 'x'; s += t; // O(1) amortized
字符串转数字 / 数字转字符串
int x = stoi(s); ll x = stoll(s);
double d = stod(s);
string t = to_string(x);
字符串流(读空格分隔)
stringstream ss(line);
while (ss >> x) ...
substr 频繁用会拷贝,大数据量用 string_view(C++17)或索引
六、pair / tuple / tie
pii p = {1, 2};
auto [x, y] = p; // C++17 结构化绑定
tuple<int,int,ll> t(1,2,3LL);
get<0>(t);
tie(x, y) = minmax(a, b); // 同时拿最小和最大
pair 自动按 first 再 second 字典序比较,排序神器
七、Lambda & 函数对象
auto cmp = [&](int i, int j) { return dist[i] < dist[j]; };
sort(v.begin(), v.end(), cmp);
// 递归 lambda(要捕获自身)
function<void(int)> dfs = [&](int u) {
for (int v : G[u]) dfs(v);
};
递归 lambda 必须用 function<> 包裹,否则捕获不到自己
八、二分模板(手写,不踩坑)
// 找第一个满足 check 的位置 [L,R]
int l = L, r = R, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
// 浮点二分
double l = 0, r = 1e9;
while (r - l > 1e-9) {
double mid = (l + r) / 2;
if (check(mid)) l = mid; else r = mid;
}
推荐永远用 l + (r-l)/2 防溢出
整数二分先想清楚边界再写 ,模板比 STL 的 lower_bound 更灵活
九、ps
| 坑 | 说明 |
|---|---|
cin 慢 |
开头必加 ios::sync_with_stdio(false); cin.tie(0); |
cin + scanf 混用 |
绝对不要,同步关了会 RE/读错 |
vector 越界 |
[] 不检查,debug 用 .at(i) |
map[key] |
不存在也会插入默认值 !判存在用 count 或 find |
priority_queue 没 clear |
赋值空队列 |
memset 非 0/-1 |
只有 0 和 -1 是安全的(按字节),其他用 fill |
size() 返回 size_t |
for(int i=0; i<v.size()-1; i++) 空容器时 size_t 下溢成巨数 |
| 多组输入忘了清空 | 全局 vector/map 不清空 = 经典 WA |
int * int 溢出 |
乘前先 1LL * a * b |
lower_bound 无序 |
不排序用二分必 WA |
bitset<N> N 不是变量 |
想动态用 vector<bool> 或手写 bitset |