参考资料
前言
最近在刷算法题的时候,经常看到大佬们的代码里都会加这么几行:
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
一开始我也不太明白这是干什么的,只是机械地复制粘贴。后来遇到一道题,数据量特别大,不加这几行就TLE(超时),加了就AC了。这才意识到这几行代码的重要性,于是专门学习了一下。
今天就来详细讲讲这三行”魔法代码”到底是什么,为什么能加速,以及使用时要注意什么。
为什么需要加速?
在C++中,有两套输入输出系统:
| 类型 | 函数 | 特点 |
|---|---|---|
| C风格 | scanf, printf |
速度快 ⚡ |
| C++风格 | cin, cout |
速度慢 🐌 |
为什么cin/cout会慢?
默认情况下,cin/cout为了兼容C语言的scanf/printf,做了很多额外的同步工作。这导致在处理大量数据时,cin/cout可能比scanf/printf慢2-3倍!
举个例子:
假设要读入100万个整数:
- 用
scanf:可能只需要0.3秒 - 用
cin(不加速):可能需要1秒 - 用
cin(加速后):也能达到0.3秒左右
在算法竞赛中,0.7秒的差距可能就是AC和TLE的区别!
三行代码详解
第一行:ios::sync_with_stdio(0)
作用:取消C/C++流的同步
默认情况(sync = true):
C++的cin/cout和C的scanf/printf是同步的,这意味着:
- ✅ 可以随意混用两种输入输出方式
- ❌ 但cin/cout要保持与scanf/printf的同步,会变慢
设置为false后:
ios::sync_with_stdio(false);
// 或者简写为
ios::sync_with_stdio(0);
- ✅ cin/cout速度大幅提升,接近scanf/printf
- ❌ 不能再混用cin/cout和scanf/printf
代码示例
// ❌ 错误用法:混用导致问题
ios::sync_with_stdio(false);
cin >> x; // C++风格
scanf("%d", &y); // C风格 - 不能这样!
printf("%d", z); // C风格 - 不能这样!
// ✅ 正确用法:统一用C++风格
ios::sync_with_stdio(false);
cin >> x;
cin >> y;
cout << x << " " << y << "\n";
形象比喻
想象有两条传送带:
同步模式(默认):
C传送带 ─┐
├─→ 合并成一条,能混用但慢
C++传送带 ─┘
不同步模式(加速):
C传送带 ──→ 各走各的,快但不能混用
C++传送带 ──→
第二行:cin.tie(0)
作用:解除cin与cout的绑定
默认情况(绑定):
cin和cout是绑定在一起的。每次使用cin输入之前,会自动刷新cout的缓冲区。
这是为了保证交互式程序的正确性:
cout << "请输入你的年龄:";
cin >> age; // 输入前会先把"请输入你的年龄:"显示出来
解除绑定后:
cin.tie(nullptr);
// 或者简写为
cin.tie(0);
- ✅ 不再自动刷新cout,速度更快
- ⚠️ 对于大多数算法题没有影响
- ⚠️ 交互题可能需要手动刷新
什么时候需要手动刷新?
对于交互题(需要实时输入输出的题目),可能需要:
cout << answer << endl; // endl会自动刷新
// 或者
cout << answer << "\n";
cout.flush(); // 手动刷新
速度提升原理
// 默认情况(绑定)
for (int i = 0; i < 1000000; ++i) {
cout << i << " "; // 输出
cin >> x; // 输入前自动刷新cout(慢!)
}
// 解除绑定后
cin.tie(nullptr);
for (int i = 0; i < 1000000; ++i) {
cout << i << " "; // 输出
cin >> x; // 不会自动刷新(快!)
}
第三行:cout.tie(0)
作用:解除cout的绑定
真相:这行基本没用!
cout默认就没有绑定任何流,所以这行代码:
cout.tie(nullptr);
写不写都一样,不会有任何性能提升。
为什么很多人还写?
主要有两个原因:
- 习惯性:复制模板时一起复制了
- 求心理安慰:反正写了也没坏处
建议
可以不写! 但写了也不会出错,看个人习惯。
完整使用模板
标准竞赛模板
#include <bits/stdc++.h>
using namespace std;
int main() {
// 输入输出加速
ios::sync_with_stdio(false);
cin.tie(nullptr);
// cout.tie(nullptr); // 这行可以省略
// 你的代码
int n;
cin >> n;
cout << n << "\n"; // 用 \n 代替 endl(更快)
return 0;
}
简化写法(逗号表达式)
int main() {
ios::sync_with_stdio(0), cin.tie(0);
// 你的代码
return 0;
}
这种写法利用了C++的逗号表达式,三个语句会依次执行,效果完全相同。
性能对比测试
我自己测试了一下,读入100万个整数的耗时:
| 方法 | 耗时 | 相对速度 |
|---|---|---|
scanf/printf |
0.28秒 | 基准(最快)⚡⚡⚡ |
cin/cout(不加速) |
0.89秒 | 慢3倍 🐌 |
cin/cout(加速) |
0.31秒 | 接近scanf ⚡⚡⚡ |
可以看到,加速后的cin/cout已经非常接近scanf/printf了!
注意事项与常见错误
⚠️ 注意1:不能混用C/C++输入输出
ios::sync_with_stdio(false);
// ✅ 可以这样
cin >> x;
cout << x;
// ❌ 不能这样
cin >> x;
printf("%d", x); // 错误!输出可能乱序
// ❌ 也不能这样
scanf("%d", &x); // 错误!
cout << x;
// ❌ getchar也不行
getchar(); // 错误!getchar是C函数
记住:加了加速后,只能用cin/cout,不能用任何C风格的输入输出函数!
⚠️ 注意2:用\n代替endl
// 推荐写法(快)
cout << x << "\n";
// 不推荐写法(慢)
cout << x << endl;
原因: endl会强制刷新缓冲区,而\n不会。在循环中大量使用endl会严重影响性能。
⚠️ 注意3:数据量小时可以不加
- 数据量 < 10,000:加不加都行,差别不大
- 数据量 > 100,000:建议加上
- 数据量 > 1,000,000:必须加,否则可能TLE
⚠️ 注意4:交互题要小心
对于需要实时交互的题目(比如猜数字游戏),可能需要手动刷新:
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 输出后需要立即显示
cout << "我猜50\n";
cout.flush(); // 手动刷新
cin >> response;
或者对于交互题,干脆不加速:
// 交互题可以不加这两行
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
cout << "我猜50" << endl; // endl会自动刷新
cin >> response;
实战案例
案例1:大量数据读入
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i]; // 快速读入
}
for (int i = 0; i < n; ++i) {
cout << a[i] << "\n"; // 快速输出
}
return 0;
}
案例2:多组测试数据
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
// 处理数据...
cout << result << "\n";
}
return 0;
}
总结
三行代码的作用总结
| 代码 | 作用 | 重要性 | 副作用 |
|---|---|---|---|
ios::sync_with_stdio(false) |
取消C/C++流同步 | ⭐⭐⭐ 很重要 | 不能混用scanf/printf |
cin.tie(nullptr) |
解除cin与cout绑定 | ⭐⭐ 有用 | 交互题需要注意 |
cout.tie(nullptr) |
解除cout绑定 | ⭐ 基本没用 | 无 |
记忆口诀
sync 同步关,cin cout 更快跑
tie 绑定解,输入输出不等待
cout.tie 没啥用,写不写都无所谓
什么时候使用?
- ✅ 算法竞赛:基本上都要加
- ✅ 大数据量题目(n > 100,000):必须加
- ⚠️ 交互题:谨慎使用,可能需要手动刷新
- ❌ 小数据量题目(n < 10,000):可以不加
最佳实践
#include <bits/stdc++.h>
using namespace std;
int main() {
// 标准竞赛模板
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 记住:
// 1. 只用 cin/cout,不用 scanf/printf
// 2. 用 \n 代替 endl
// 3. 不要混用 C/C++ 输入输出
// 你的代码...
return 0;
}