读写加速
机考输入加速2.0(fread版):全类型快读总结
基于fread的快读2.0核心是“全局缓冲区预读+分类型解析”,解决机考中int、long long(LL)、字符串(含/不含空格)的高效读取问题,配合快写可实现输入输出双加速,彻底避免IO超时。以下是完整实现、示例用法及机考注意事项。
一、核心原理
通过fread一次性将输入数据读入全局内存缓冲区(默认1MB,足够覆盖绝大多数机考输入规模),后续读取各类型数据时直接从缓冲区取字符,避免频繁系统调用(IO操作中最耗时的环节)。每个数据类型的快读函数均基于缓冲区实现,逻辑独立且易用。
二、完整实现(加速2.0模板)
#include <cstdio>
#include <cstring> // 用于memset(字符串快读用)
using namespace std;
// -------------------------- 全局缓冲区(所有快读函数共用) --------------------------
const int BUF_SIZE = 1 << 20; // 1MB缓冲区(机考推荐大小,可按需调至1<<25=32MB)
char buf[BUF_SIZE]; // 存储预读的输入数据
int buf_ptr = 0; // 缓冲区当前读取指针(指向下次要读的字符)
int buf_len = 0; // 缓冲区中已预读的数据长度
// 辅助函数:从缓冲区取1个字符(缓冲区空则自动用fread补满)
inline char my_getc() {
if (buf_ptr >= buf_len) { // 缓冲区已读完,重新预读
buf_len = fread(buf, 1, BUF_SIZE, stdin);
buf_ptr = 0;
if (buf_len == 0) return EOF; // 输入结束(避免死循环)
}
return buf[buf_ptr++]; // 返回当前字符并移动指针
}
// -------------------------- 1. 快读int(处理正负整数,跳过空白符) --------------------------
inline int read_int() {
int x = 0;
int sign = 1; // 符号位(1为正,-1为负)
char ch = my_getc();
// 第一步:跳过所有空白符(空格、换行、制表符等,机考输入常含多余空白)
while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') {
ch = my_getc();
}
// 第二步:处理负号(若有)
if (ch == '-') {
sign = -1;
ch = my_getc();
}
// 第三步:读取数字并累积
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = my_getc();
}
return x * sign;
}
// -------------------------- 2. 快读long long(LL,同int逻辑,仅返回类型不同) --------------------------
inline long long read_LL() {
long long x = 0;
int sign = 1;
char ch = my_getc();
// 跳过空白符
while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') {
ch = my_getc();
}
// 处理负号
if (ch == '-') {
sign = -1;
ch = my_getc();
}
// 累积数字(用long long避免溢出)
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = my_getc();
}
return x * sign;
}
// -------------------------- 3. 快读无空格字符串(如单词、标识符,遇空白符停止) --------------------------
// 功能:读取不含空格、换行的字符串,存入str数组,返回字符串长度
// 参数:str:目标字符数组(需提前分配足够空间,机考建议1e5+);max_len:str最大长度(防止溢出)
inline int read_str(char str[], int max_len) {
int len = 0;
char ch = my_getc();
// 第一步:跳过前导空白符(如输入开头的空格、换行)
while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') {
ch = my_getc();
}
// 第二步:读取字符(直到遇到空白符或达到数组最大长度)
while (ch != ' ' && ch != '\n' && ch != '\r' && ch != '\t' && ch != EOF && len < max_len - 1) {
str[len++] = ch;
ch = my_getc();
}
// 第三步:添加字符串结束符(C风格字符串必需)
str[len] = '\0';
return len;
}
// -------------------------- 4. 快读有空字符串(如整行输入,含空格,遇换行停止) --------------------------
// 功能:读取一整行(包括空格),存入str数组,自动过滤末尾的'\n'
// 参数:str:目标字符数组;max_len:str最大长度
inline int read_line(char str[], int max_len) {
int len = 0;
char ch = my_getc();
// 读取字符(直到遇到换行或EOF,且不超过数组最大长度)
while (ch != '\n' && ch != EOF && len < max_len - 1) {
str[len++] = ch;
ch = my_getc();
}
// 处理换行符(若存在,跳过,避免下一次读取受影响)
if (ch == '\n') {
buf_ptr++; // 跳过'\n'(my_getc()已读过ch,需手动移动指针)
}
// 添加字符串结束符
str[len] = '\0';
return len;
}
// -------------------------- 配套快写(机考输出加速必备,避免输出超时) --------------------------
char out_buf[BUF_SIZE]; // 输出缓冲区
int out_ptr = 0; // 输出缓冲区指针
// 辅助:写入单个字符到缓冲区
inline void putc(char c) {
if (out_ptr >= BUF_SIZE) { // 缓冲区满,批量写入stdout
fwrite(out_buf, 1, out_ptr, stdout);
out_ptr = 0;
}
out_buf[out_ptr++] = c;
}
// 快写int
inline void write_int(int x) {
if (x < 0) { putc('-'); x = -x; }
if (x == 0) { putc('0'); return; }
char stk[20]; int top = 0;
while (x) { stk[top++] = x % 10 + '0'; x /= 10; }
while (top--) { putc(stk[top]); }
}
// 快写long long
inline void write_LL(long long x) {
if (x < 0) { putc('-'); x = -x; }
if (x == 0) { putc('0'); return; }
char stk[20]; int top = 0;
while (x) { stk[top++] = x % 10 + '0'; x /= 10; }
while (top--) { putc(stk[top]); }
}
// 快写字符串
inline void write_str(const char str[]) {
int i = 0;
while (str[i] != '\0') {
putc(str[i++]);
}
}
// 快写换行符
inline void write_endl() {
putc('\n');
}
// 关键:刷新输出缓冲区(程序结束前必须调用,否则内容可能残留)
inline void flush_out() {
if (out_ptr > 0) {
fwrite(out_buf, 1, out_ptr, stdout);
out_ptr = 0;
}
}
三、机考示例用法(完整流程)
以下示例模拟机考常见场景:读取多种类型输入,处理后用快写输出,覆盖所有快读/快写函数的使用。
int main() {
// -------------------------- 1. 读取int示例:读n个整数,输出总和 --------------------------
int n = read_int();
int sum_int = 0;
for (int i = 0; i < n; i++) {
int x = read_int();
sum_int += x;
}
write_str("int总和:");
write_int(sum_int);
write_endl();
// -------------------------- 2. 读取LL示例:读m个长整数,输出最大值 --------------------------
int m = read_int();
long long max_LL = -1e18;
for (int i = 0; i < m; i++) {
long long x = read_LL();
if (x > max_LL) max_LL = x;
}
write_str("LL最大值:");
write_LL(max_LL);
write_endl();
// -------------------------- 3. 读取无空格字符串示例:读2个单词,输出拼接结果 --------------------------
char str1[100], str2[100];
read_str(str1, 100); // 读第一个单词(如"Hello")
read_str(str2, 100); // 读第二个单词(如"World")
write_str("拼接字符串:");
write_str(str1);
write_str(" ");
write_str(str2);
write_endl();
// -------------------------- 4. 读取有空字符串示例:读1行句子,输出长度 --------------------------
char line[1000];
int line_len = read_line(line, 1000); // 读整行(如"I love programming")
write_str("句子长度:");
write_int(line_len);
write_endl();
write_str("你输入的句子:");
write_str(line);
write_endl();
// -------------------------- 关键:刷新输出缓冲区 --------------------------
flush_out();
return 0;
}
输入示例:
3 1 2 3 // n=3,3个int:1,2,3
2 10000000000 5 // m=2,2个LL:1e10,5
Hello World // 2个无空格字符串
I love programming// 1行有空字符串
输出示例:
int总和:6
LL最大值:10000000000
拼接字符串:Hello World
句子长度:18
你输入的句子:I love programming
四、机考注意事项(避坑指南)
- 缓冲区刷新必做:快写的
flush_out()必须在main函数结束前调用,否则缓冲区中残留的输出内容会丢失(如最后几行结果不显示)。 - 字符串数组大小:
read_str和read_line的字符数组需提前分配足够空间(机考建议至少1e5,避免输入过长导致数组越界)。 - 空白符处理:所有快读函数均自动跳过前导空白符(空格、换行),无需手动处理输入中的多余空白,适配机考输入格式。
- 类型匹配:
read_int对应int,read_LL对应long long,避免混用(如用read_int读1e18会溢出)。 - 编译器兼容性:代码基于C++标准,机考中GCC、Clang、MSVC均兼容,无需额外配置。
五、优势总结(为何称“加速2.0”)
| 对比维度 | 普通cin/cout(关闭同步) | 基础getchar/putchar快读 | 本版fread快读2.0 |
|---|---|---|---|
| 系统调用次数 | 多(每次读/写均可能触发) | 较多(每次1个字符) | 极少(1次预读) |
| 支持类型 | 全,但慢 | 仅int/LL | int/LL/字符串全支持 |
| 机考适配性 | 易超时(大数据量) | 部分场景适用 | 全场景高效,无超时风险 |
| 易用性 | 高 | 低(需重复写逻辑) | 高(模板化,直接调用) |
本模板可直接作为机考“输入输出工具库”,复制到代码开头即可使用,彻底解决IO效率问题,专注算法逻辑实现。
浙公网安备 33010602011771号