编程小技巧,小知识
这一篇把做题时反复会用到、但每次都要重新查的几条「代码模板」和「容易踩坑」记一下,每个都附最简可运行例子。
一、读整数直到文件结束
OJ(在线判题)里经常出现「不告诉你有多少个输入,要读到文件尾」的情况。模板:
1 | int value; |
同理可读 string、double、char:
1 | string s; |
注意点:cin >> 会跳过空白字符(空格、换行、制表符),所以一行多个数或一行一个数都能正确读,不需要额外处理。
输入完之后,按: Ctrl + Z,然后再敲一下回车 → 触发 EOF,循环结束。
Linux/Mac 是
Ctrl+D
二、循环 n 次
while (n--) 是最简洁的写法:
1 | int n = 5; |
也可以反过来——但务必分清「先判断再减」和「先减再判断」的区别:
| 写法 | 行为 |
|---|---|
while (n--) { ... } |
先用 n 的当前值判非零,再 n - 1;循环体执行 n 次 |
while (--n) { ... } |
先 n - 1,再判非零;循环体执行 n - 1 次 |
do { ... } while (n--); |
至少执行一次 |
注意点:n 是 unsigned 类型时,n-- 到 0 之后继续减会变成巨大的正数——while (n--) 在 unsigned 上会死循环。永远把循环计数用 int / long long。
三、提速输入输出
cin / cout 默认会跟 C 标准的 scanf / printf 保持同步,方便混合使用,但慢。比赛里数据一大,必开这行:
1 | ios::sync_with_stdio(false); |
sync_with_stdio(false):解开cin和scanf的同步,两者不再互通(开加速后不要混用scanf);cin.tie(nullptr):解开cin与cout的绑定,>>时不再每次 flushcout。
效果:通常能把 10⁵ 量级输入输出的耗时从秒级压到几十毫秒。
记法:
1 | sync synchronize 同步 |
四、闭区间 [L, R] 的长度
1 | int len = R - L + 1; // 闭区间长度 |
很多人下意识写 R - L,少 1。这是二分答案、单点改区间求和时最常见的 bug。
1 | 区间 长度 |
半开 vs 闭区间:
| 表示 | 区间类型 | 长度 |
|---|---|---|
[L, R] |
闭 | R - L + 1 |
[L, R) |
左闭右开 | R - L |
for (int i = 0; i < n; i++) |
i ∈ [0, n) |
n |
STL 的 begin() / end()、C++ 数组下标都习惯用左闭右开,写循环时记得统一。
五、其他几条高频片段
5.1 读一整行带空格的字符串
cin >> s 一遇到空格就停,要读整行用 getline:
1 | string s; |
坑:如果前面用过 cin >>,缓冲区里会残留一个换行符,先 cin.ignore() 一次再 getline:
1 | int n; |
5.2 交换两个变量
1 | swap(a, b); // STL 标准库自带,<utility>,已经通过 using namespace std 可直接用 |
不要自己写 int t = a; a = b; b = t;——std::swap 对自定义类型也可以通过特化获得更好的实现。
5.3 求最大值/最小值
1 | int mx = max({a, b, c}); // initializer_list 重载,需要 C++11 |
对 vector 取极值:
1 | int mx = *max_element(v.begin(), v.end()); |
5.4 排序 + 找下标
1 | sort(v.begin(), v.end()); // 默认升序 |
六、易错点清单
- 整数除法向下取整:
int x = 5 / 2;得2,不是2.5。要真除法先(double)a / b。 size_t与-1:(int)v.size() - 1在空 vector 时会变成INT_MAX。先判empty()。- 死循环的
while (n--):n是unsigned,且初值是0,会变成UINT_MAX然后无限循环。 - 开加速后混用
scanf:ios::sync_with_stdio(false)后cin和scanf不再互通,会读到错位的数据。要么全cin,要么全scanf。 int溢出:10⁹ 级别的乘、加法经常超int。看到10⁹ × 10⁹这种直接long long。endl比'\n'慢:endl每次都 flush 输出缓冲,'\n'不会。比赛里要快,直接'\n'。
七、小结
这页没什么高深东西,目的就是把零散在脑子里的小片段集中起来,下次写新题时能直接复制粘贴。
| 用途 | 一行模板 |
|---|---|
| 读到 EOF | while (cin >> x) { ... } |
| 循环 n 次 | while (n--) { ... } |
| 提速 | ios::sync_with_stdio(false); cin.tie(nullptr); |
| 闭区间长度 | R - L + 1 |
| 求极值 | max({a, b, c}) / *max_element(v.begin(), v.end()) |
| 排序 | sort(v.begin(), v.end()) |
函数:(y=\log_2(x+1))
y向上取整的结果为x的位数
模运算加法性质:加法取模可以分步取模
1 | 模运算加法性质:**加法取模可以分步取模** |
1 | a*b%p=a*b-向下取整(a*b/p)*p |
向上取整
1 | 向上取整(a/b)=(a+b-1)/b; |
