
在算法竞赛中,两大 “隐形杀手” 常常让选手功亏一篑 —— 一是大规模数据下的 IO 超时,二是超 long long 范围的数值溢出。当输入数据量突破 1e6 级别,
cin/cout的同步开销会成为致命瓶颈;当数值运算超出 9e18(long long 上限),常规整型根本无力承载。而快速读写技术能让 IO 速度翻倍,__int128 则能轻松应对超大规模整数运算。本文将从原理到实战,详解这两大 “极限突破” 工具的使用技巧,,让你在处理大数据和大数值问题时游刃有余。下面就让我们正式开始吧!
在算法竞赛中,很多选手会遇到 “算法正确但超时” 的窘境,其中八成是 IO 效率太低导致的。尤其是当输入数据量达到 1e5 甚至 1e6 级别时,cin和cout的默认同步模式会因为频繁的缓冲区交互而严重拖慢速度。快速读写技术通过直接操作字符流,跳过冗余开销,能将 IO 效率提升 5~10 倍,成为处理大数据的必备技能。
常规 IO(cin/cout/scanf/printf)的效率瓶颈在于:
cin/cout默认与 C 语言标准 IO 同步,存在大量缓冲区同步开销;scanf/printf虽然比cin/cout快,但仍有格式解析的额外消耗。快速读写的核心思路是 “绕过冗余层,直接操作字符流”:
getchar()逐个读取字符,putchar()逐个输出字符,避免格式解析开销;ret = ret * 10 + ch - '0')实时将字符序列转换为整数,无需临时存储字符串;快速读入的核心是处理正负号、跳过非数字字符、用秦九韶算法转换数值:
#include <iostream>
using namespace std;
// 快速读入整数(支持正负,兼容Windows/Linux)
inline int read() {
int ret = 0; // 存储最终结果
int flag = 1; // 标记正负,默认正数
char ch = getchar(); // 读取第一个字符
// 跳过非数字字符(空格、换行、制表符等)
while (ch < '0' || ch > '9') {
if (ch == '-') flag = -1; // 遇到负号,标记为负数
ch = getchar();
}
// 读取数字字符,用秦九韶算法转换为整数
while (ch >= '0' && ch <= '9') {
ret = ret * 10 + (ch - '0'); // 秦九韶算法:逐步累加数值
ch = getchar();
}
return ret * flag; // 返回带符号的结果
}
int main() {
int n = read(); // 快速读入数据规模
long long sum = 0;
for (int i = 0; i < n; ++i) {
sum += read(); // 快速读入n个整数并求和
}
cout << sum << endl;
return 0;
}快速输出的核心是递归处理高位数字,再逐个输出字符:
#include <iostream>
using namespace std;
// 快速输出整数(支持正负,兼容Windows/Linux)
inline void print(int x) {
if (x < 0) { // 处理负数:先输出负号,再转为正数
putchar('-');
x = -x;
}
// 递归输出高位数字(如x=123,先输出1,再输出2,最后输出3)
if (x > 9) print(x / 10);
putchar(x % 10 + '0'); // 输出当前位数字(转换为字符)
}
int main() {
int x = -123456;
print(x); // 输出:-123456
putchar('\n'); // 手动换行(putchar效率极高)
return 0;
}getchar_unlocked(Linux 专用) 在 Linux 系统中,getchar_unlocked()和putchar_unlocked()函数去掉了线程安全锁,速度比getchar()快 30% 以上。但需注意:该函数不支持 Windows 系统,且在多线程环境下可能出现问题(竞赛中通常为单线程,可放心使用)。
#include <iostream>
using namespace std;
// Linux专用快速读入(无锁版,速度更快)
inline int read_unlocked() {
int ret = 0, flag = 1;
char ch = getchar_unlocked();
while (ch < '0' || ch > '9') {
if (ch == '-') flag = -1;
ch = getchar_unlocked();
}
while (ch >= '0' && ch <= '9') {
ret = ret * 10 + (ch - '0');
ch = getchar_unlocked();
}
return ret * flag;
}
// Linux专用快速输出(无锁版,速度更快)
inline void print_unlocked(int x) {
if (x < 0) {
putchar_unlocked('-');
x = -x;
}
if (x > 9) print_unlocked(x / 10);
putchar_unlocked(x % 10 + '0');
}
int main() {
int n = read_unlocked();
long long sum = 0;
for (int i = 0; i < n; ++i) {
sum += read_unlocked();
}
print_unlocked(sum);
putchar_unlocked('\n');
return 0;
}场景 | 常规 IO(cin/cout) | 快速读写(基础版) | 快速读写(Linux 无锁版) |
|---|---|---|---|
数据量 1e5 | 可能超时 | 毫秒级完成 | 微秒级完成 |
数据量 1e6 | 必然超时 | 快速完成 | 极快完成 |
跨平台兼容性 | 好(Windows/Linux) | 好 | 差(仅 Linux) |
实现复杂度 | 低(直接调用) | 中(自定义函数) | 中 |
核心优势:对于 1e6 级别的整数输入,快速读写的速度可达cin的 5~10 倍,scanf的 2~3 倍,能有效避免因 IO 导致的超时,为算法本身节省宝贵时间。
题目链接:https://www.luogu.com.cn/problem/P10815
题目描述:输入 n 个整数,求和并输出结果。
输入描述:第一行一个整数 n,第二行 n 个整数(可能为负)。
输出描述:一行一个整数,表示总和。
示例输入:5 -1 2 -3 4 -5 → 输出:-3。
#include <iostream>
using namespace std;
// 快速读入(兼容Windows/Linux)
inline int read() {
int ret = 0, flag = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') flag = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
ret = ret * 10 + (ch - '0');
ch = getchar();
}
return ret * flag;
}
// 快速输出(兼容Windows/Linux)
inline void print(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int main() {
int n = read();
int sum = 0;
for (int i = 0; i < n; ++i) {
sum += read();
}
print(sum);
putchar('\n');
return 0;
} 当题目中数值范围超过long long(最大约 9e18)时,常规整型会发生溢出,导致结果错误。而__int128是 GCC 编译器支持的 128 位整型,能存储范围为-2^127 ~ 2^127-1(约 - 1e38 ~ 1e38),可作为高精度算法的 “轻量化替代方案”,无需复杂的高精度模板就能处理超大规模整数运算。
long long完全一致;long long,但无需高精度算法的场景(如大整数乘法取模、数论中的大模数运算)。cin/cout/scanf/printf直接读写,必须结合快速读写实现;long long为 8 字节),大规模数组使用可能增加内存消耗; 由于__int128无法直接通过常规 IO 函数读写,需自定义读写函数,本质是将 128 位整数视为字符流处理,与快速读写原理一致:
#include <iostream>
using namespace std;
// 定义__int128为LL,简化书写
typedef __int128 LL;
// 快速读入__int128(支持正负)
inline LL read_int128() {
LL ret = 0;
int flag = 1;
char ch = getchar();
// 跳过非数字字符
while (ch < '0' || ch > '9') {
if (ch == '-') flag = -1;
ch = getchar();
}
// 秦九韶算法转换为__int128
while (ch >= '0' && ch <= '9') {
ret = ret * 10 + (ch - '0');
ch = getchar();
}
return ret * flag;
}
// 快速输出__int128(支持正负)
inline void print_int128(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
// 递归输出高位数字
if (x > 9) print_int128(x / 10);
putchar(x % 10 + '0');
}
int main() {
LL a = read_int128();
LL b = read_int128();
print_int128(a * b); // 输出两个大整数的乘积(无溢出)
putchar('\n');
return 0;
} 当两个long long级别的数相乘(如 1e18 × 1e18 = 1e36),long long会溢出,而__int128可直接计算后取模:
#include <iostream>
using namespace std;
typedef __int128 LL;
const LL MOD = 1e9 + 7;
// 快速读入__int128
inline LL read() {
LL ret = 0, flag = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') flag = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
ret = ret * 10 + (ch - '0');
ch = getchar();
}
return ret * flag;
}
// 快速输出__int128
inline void print(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int main() {
LL a = read(); // 输入1e18
LL b = read(); // 输入1e18
LL res = (a * b) % MOD; // 直接计算,无溢出
print(res); // 输出:(1e18 * 1e18) mod 1e9+7 = 0
putchar('\n');
return 0;
} 在 China Remainder Theorem(CRT)中,当多个模数乘积超过long long范围时,__int128可轻松承载:
#include <iostream>
using namespace std;
typedef __int128 LL;
// 计算多个模数的乘积(可能超long long)
LL mul_mods(LL mods[], int n) {
LL product = 1;
for (int i = 0; i < n; ++i) {
product *= mods[i]; // __int128无溢出
}
return product;
}
// 快速输出__int128
inline void print(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int main() {
LL mods[] = {1e9, 1e9, 1e9, 1e9}; // 4个1e9相乘=1e36
LL product = mul_mods(mods, 4);
print(product); // 输出:10000000000000000000000000000000000000
putchar('\n');
return 0;
} 在快速幂、欧拉降幂等运算中,中间结果可能超long long,__int128可作为临时存储载体:
#include <iostream>
using namespace std;
typedef __int128 LL;
// 快速幂:计算(a^b) mod mod,用__int128避免中间溢出
LL qpow(LL a, LL b, LL mod) {
LL ret = 1;
a %= mod;
while (b) {
if (b & 1) ret = (ret * a) % mod; // __int128承载中间乘积
a = (a * a) % mod;
b >>= 1;
}
return ret;
}
// 快速读入__int128
inline LL read() {
LL ret = 0, flag = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') flag = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
ret = ret * 10 + (ch - '0');
ch = getchar();
}
return ret * flag;
}
// 快速输出__int128
inline void print(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int main() {
LL a = read(); // 输入1e9
LL b = read(); // 输入1e9
LL mod = read(); // 输入1e9+7
LL res = qpow(a, b, mod);
print(res); // 输出:1e9^1e9 mod 1e9+7
putchar('\n');
return 0;
}特性 | __int128 | 手写高精度算法(vector 模拟) |
|---|---|---|
存储范围 | -1e38 ~ 1e38(有限) | 理论上无上限(依赖内存) |
运算速度 | 极快(编译器优化) | 较慢(多次循环模拟运算) |
实现复杂度 | 低(用法同 long long) | 高(需实现加减乘除取模等) |
IO 支持 | 需自定义快速读写 | 需自定义字符串转换 |
适用场景 | 数值略超 long long | 数值极大(如 100 位以上) |
选择策略:
当题目同时涉及 “大规模数据 IO” 和 “超 long long 数值运算” 时,需结合快速读写和__int128,才能兼顾效率和正确性。以下以 “超大数求和” 为例,展示综合应用:
输入 n 个超大整数(每个数不超过 30 位十进制数),求和并输出结果。
第一行一个整数 n(1≤n≤1e5),第二行 n 个超大整数(可能为负)。
一行一个超大整数,表示总和。
#include <iostream>
using namespace std;
typedef __int128 LL;
// 快速读入__int128(处理30位超大数)
inline LL read() {
LL ret = 0;
int flag = 1;
char ch = getchar();
// 跳过非数字字符
while (ch < '0' || ch > '9') {
if (ch == '-') flag = -1;
ch = getchar();
}
// 处理30位数字,秦九韶算法转换
while (ch >= '0' && ch <= '9') {
ret = ret * 10 + (ch - '0');
ch = getchar();
}
return ret * flag;
}
// 快速输出__int128
inline void print(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); // 可选:关闭同步,进一步提升速度
int n = 0;
char ch = getchar();
while (ch >= '0' && ch <= '9') {
n = n * 10 + (ch - '0');
ch = getchar();
} // 快速读入n(避免混用IO函数)
LL sum = 0;
for (int i = 0; i < n; ++i) {
sum += read(); // 快速读入超大数并累加
}
print(sum);
putchar('\n');
return 0;
}误区 1:忘记处理负数:快速读入时未判断负号,导致负数读取错误;
flag=-1;误区 2:混用 IO 函数:快速读写与cin/scanf混用,导致缓冲区冲突;
getchar()/putchar(),不混用其他 IO 函数;误区 3:递归深度溢出:快速输出时,超大数(如 1e30)递归深度达 30 层,导致栈溢出;
避坑:对于超大规模数字,可将递归改为迭代输出(如下):
inline void print_iter(LL x) {
char buf[40]; // 存储数字字符(最多39位)
int top = 0;
if (x < 0) {
putchar('-');
x = -x;
}
do {
buf[top++] = x % 10 + '0';
x /= 10;
} while (x);
// 反向输出(buf中是逆序存储)
for (int i = top - 1; i >= 0; --i) {
putchar(buf[i]);
}
}cout/printf输出__int128,导致编译错误; __int128 arr[1e7],导致内存溢出(1e7×16 字节 = 160MB); long long,避免内存浪费。getchar_unlocked()不可用,快速读写需用基础版;__int128 需用 MinGW 编译器(Code::Blocks、Dev-C++ 默认集成);getchar_unlocked()和__int128,可使用极限优化版;快速读写和__int128 是算法竞赛中处理 “大数据 IO” 和 “大数值运算” 的两大核心工具,如果在学习过程中遇到具体题目无法解决,或想了解快速读写与高精度算法的结合应用,可以随时留言交流。后续将持续更新数论进阶内容,敬请关注!