这一篇把做题时反复会用到、但每次都要重新查的几条「代码模板」和「容易踩坑」记一下,每个都附最简可运行例子。

一、读整数直到文件结束

OJ(在线判题)里经常出现「不告诉你有多少个输入,要读到文件尾」的情况。模板:

1
2
3
4
int value;
while (cin >> value) { // 读不到就结束(文件尾 / 类型不符 / EOF)
// 在这里处理 value
}

同理可读 string、double、char:

1
2
string s;
while (cin >> s) { /* ... */ }

注意点:cin >> 会跳过空白字符(空格、换行、制表符),所以一行多个数或一行一个数都能正确读,不需要额外处理。

输入完之后,按: Ctrl + Z,然后再敲一下回车 → 触发 EOF,循环结束。

Linux/Mac 是 Ctrl+D

二、循环 n 次

while (n--) 是最简洁的写法:

1
2
3
4
int n = 5;
while (n--) {
cout << n << ' '; // 输出 4 3 2 1 0
}

也可以反过来——但务必分清「先判断再减」和「先减再判断」的区别:

写法 行为
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
2
ios::sync_with_stdio(false);
cin.tie(nullptr);
  • sync_with_stdio(false):解开 cin 和 scanf 的同步,两者不再互通(开加速后不要混用 scanf);
  • cin.tie(nullptr):解开 cin 与 cout 的绑定,>> 时不再每次 flush cout。

效果:通常能把 10⁵ 量级输入输出的耗时从秒级压到几十毫秒。

记法:

1
2
3
4
sync   synchronize  同步
stdio standard input/output
tie 绑定
async asynchronous 异步(反义词)

四、闭区间 [L, R] 的长度

1
int len = R - L + 1;        // 闭区间长度

很多人下意识写 R - L,少 1。这是二分答案、单点改区间求和时最常见的 bug。

1
2
3
4
区间    长度
[0, 0] 1 → 0 - 0 + 1 = 1 ✅
[1, 5] 5 → 5 - 1 + 1 = 5 ✅
[0, n) n → 这是半开区间,长度 n(不加 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
2
string s;
getline(cin, s); // 读一行(不含行尾换行)

坑:如果前面用过 cin >>,缓冲区里会残留一个换行符,先 cin.ignore() 一次再 getline:

1
2
3
4
5
int n;
cin >> n;
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 丢掉残留换行
string line;
getline(cin, line);

5.2 交换两个变量

1
swap(a, b);                  // STL 标准库自带,<utility>,已经通过 using namespace std 可直接用

不要自己写 int t = a; a = b; b = t;——std::swap 对自定义类型也可以通过特化获得更好的实现。

5.3 求最大值/最小值

1
2
int mx = max({a, b, c});     // initializer_list 重载,需要 C++11
int mn = min({a, b, c});

对 vector 取极值:

1
2
int mx = *max_element(v.begin(), v.end());
int mn = *min_element(v.begin(), v.end());

5.4 排序 + 找下标

1
2
sort(v.begin(), v.end());                          // 默认升序
sort(v.begin(), v.end(), greater<int>()); // 降序

六、易错点清单

  1. 整数除法向下取整:int x = 5 / 2; 得 2,不是 2.5。要真除法先 (double)a / b。
  2. size_t 与 -1:(int)v.size() - 1 在空 vector 时会变成 INT_MAX。先判 empty()。
  3. 死循环的 while (n--):n 是 unsigned,且初值是 0,会变成 UINT_MAX 然后无限循环。
  4. 开加速后混用 scanf:ios::sync_with_stdio(false) 后 cin 和 scanf 不再互通,会读到错位的数据。要么全 cin,要么全 scanf。
  5. int 溢出:10⁹ 级别的乘、加法经常超 int。看到 10⁹ × 10⁹ 这种直接 long long。
  6. 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
2
模运算加法性质:**加法取模可以分步取模**
\((a+b+c)\bmod p=\big[(a+b)\bmod p + c\big]\bmod p\)
1
a*b%p=a*b-向下取整(a*b/p)*p

向上取整

1
向上取整(a/b)=(a+b-1)/b;