参考资料


前言

最近在刷算法题的时候,经常看到大佬们的代码里都会加这么几行:

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);

写不写都一样,不会有任何性能提升。

为什么很多人还写?

主要有两个原因:

  1. 习惯性:复制模板时一起复制了
  2. 求心理安慰:反正写了也没坏处

建议

可以不写! 但写了也不会出错,看个人习惯。


完整使用模板

标准竞赛模板

#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;
}