快读快写 - 学习笔记

在OI中,经常有输入输出量巨大的题,这一类题一般需要非常快速的输入输出方式,于是便有了快读快写

下面是模板(原理无需理解,用的时候直接复制上就行):

注意:程序末尾一定要刷新缓存区!

#include <cstdio>
#include <cctype>
using namespace std;
int precision=-1;
char buf[100000],*p1=buf,*p2=buf;
#define nextChar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
inline int scan_int(){
    int x=0,f=1;
    char ch=nextChar();
    while(ch<48||ch>57){
        if(ch=='-')f=-1;
        ch=nextChar();
    }
    while(ch>=48&&ch<=57){
        x=(x<<3)+(x<<1)+(ch^48);
        ch=nextChar();
    }
    return x*f;
}
inline double power(double a,int k){
	double res=1;
	while(k){
		if(k&1) res*=a;
		a=a*a;
		k>>=1;
	}
	return res;
}
inline double scan_double(){
    double x=0,y=0;
    int f=1,mine=0;
    char ch=nextChar();
    while(ch<48||ch>57){
        if(ch=='-')f=-1;
        ch=nextChar();
    }
    while(ch>=48&&ch<=57){
        x=x*10+(ch^48);
        ch=nextChar();
    }
    if(ch=='.'){
        ch=nextChar();
        while(ch>=48&&ch<=57){
            y=y*10+(ch^48);
            mine++;
            ch=nextChar();
        }
    }
    precision=mine;
    return f*(x+y/power(10,mine));
}
inline bool scan_bool(){
    int x=scan_int();
    return x!=0;
}
inline char scan_char(){
    char ch=nextChar();
    while(isspace(ch)) ch=nextChar();
    return ch;
}
const int BUF_SIZE=1<<20;
char output_buffer[BUF_SIZE],*output_ptr=output_buffer;
inline void flush_output(){
    fwrite(output_buffer,1,output_ptr-output_buffer,stdout);
    output_ptr=output_buffer;
}
inline void print_int(int x){
    if(x<0){
        *output_ptr++='-';
        x=-x;
    }
    char temp[20];
    int len=0;
    do{
        temp[len++]='0'+(x%10);
        x/=10;
    }while(x);
    while(len--) *output_ptr++=temp[len];
    if(output_ptr-output_buffer>=BUF_SIZE-20) flush_output();
}
inline void print_double(double x){
    char temp[30];
    int len;
    if(precision<0) len=snprintf(temp,sizeof(temp),"%.f",x);
    else len=snprintf(temp,sizeof(temp),"%.*f",precision,x);
    for(int i=0;i<len;i++) *output_ptr++=temp[i];
    if(output_ptr-output_buffer>=BUF_SIZE-20) flush_output();
}
inline void print_bool(bool x){
    const char *str=x?"true":"false";
    while(*str) *output_ptr++=*str++;
    if(output_ptr-output_buffer>=BUF_SIZE-20) flush_output();
}
inline void print_char(char x){
    *output_ptr++=x;
    if(output_ptr-output_buffer>=BUF_SIZE-20) flush_output();
}
int main(){
    int a=scan_int();
    double b=scan_double();
    bool c=scan_bool();
    char d=scan_char();
    print_int(a);
    print_char('\n');
    print_double(b);
    print_char('\n');
    print_bool(c);
    print_char('\n');
    print_char(d);
    print_char('\n');
    flush_output();
    return 0;
}

例题1:洛谷 - P10815 【模板】快速读入

题目链接

实际难度:\(\color{F39C11}{{普及-}}\)

考察知识点

思路分析

模板题

复杂度分析

时间复杂度

\[\begin{aligned} T(n)&=\underbrace{O(n)}_{输入}+\underbrace{O(1)}_{计算}+\underbrace{O(1)}_{输出} \\ &=O(1) \end{aligned} \]

空间复杂度

  • 主要存储:\(O(1)\)
  • 临时变量:\(O(1)\)
  • 总空间:\(O(1)\)

C++代码

// Problem: P10815 【模板】快速读入
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P10815
// Memory Limit: 2 MB
// Time Limit: 2500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// 包含标准输入输出库,用于fread、fwrite等底层IO函数
#include <cstdio>
// 包含字符分类函数库,用于isspace(判断空白字符)等函数
#include <cctype>
using namespace std;

// 全局变量:记录浮点数的小数位数(精度),初始为-1表示未指定精度
int precision = -1;
// 输入缓冲区:大小100000字节,用于批量读取输入数据(减少IO次数,提高速度)
char buf[100000];
// 缓冲区指针:p1指向当前读取位置,p2指向缓冲区末尾(已读取数据的边界)
char *p1 = buf, *p2 = buf;

// 宏定义nextChar:从输入缓冲区读取下一个字符,缓冲区空则用fread填充
// 逻辑:若p1==p2(缓冲区已读完),则用fread从stdin读100000字节到buf,更新p1和p2
// 若仍无数据(p1==p2,说明到达EOF),返回EOF;否则返回p1指向的字符并让p1后移
#define nextChar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)

// 快速读取整数函数:处理正负整数,比scanf更快(减少格式化解析开销)
inline int scan_int() {
    int x = 0;    // 存储读取的整数结果,初始为0
    int f = 1;    // 符号标志:1表示正数,-1表示负数,初始为正
    char ch = nextChar();  // 读取第一个字符
    
    // 跳过非数字字符(直到遇到数字或负号)
    while (ch < 48 || ch > 57) {  // 48是'0'的ASCII,57是'9'的ASCII,此条件判断非数字
        if (ch == '-') f = -1;    // 若遇到负号,将符号标志设为-1
        ch = nextChar();          // 继续读取下一个字符
    }
    
    // 读取数字部分,计算整数结果
    while (ch >= 48 && ch <= 57) {  // 仅处理数字字符
        // x = x*10 + (ch-'0'):位运算优化(x<<3是x*8,x<<1是x*2,总和x*10)
        // ch^48等价于ch-'0'(因'0'ASCII=48,异或后低4位为数字值)
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = nextChar();  // 继续读取下一个字符
    }
    
    return x * f;  // 返回带符号的整数结果
}

// 快速幂函数:计算a的k次幂(用于浮点数小数部分的分母计算,a=10,k=小数位数)
inline double power(double a, int k) {
    double res = 1;  // 结果初始为1(乘法单位元)
    while (k) {      // 当指数k不为0时循环
        if (k & 1)   // 若k为奇数(二进制最低位为1),将当前a乘到结果中
            res *= a;
        a = a * a;   // a自乘,指数翻倍(对应二进制右移一位)
        k >>= 1;     // k右移一位(等价于k=k/2,整数除法)
    }
    return res;  // 返回a^k的结果
}

// 快速读取浮点数函数:处理整数部分、小数部分,支持正负,记录精度
inline double scan_double() {
    double x = 0;   // 存储浮点数的整数部分,初始为0
    double y = 0;   // 存储浮点数的小数部分,初始为0
    int f = 1;      // 符号标志:1正-1负,初始为正
    int mine = 0;   // 记录小数位数,初始为0
    
    char ch = nextChar();  // 读取第一个字符
    
    // 跳过非数字字符(直到遇到数字、负号或小数点)
    while (ch < 48 || ch > 57) {
        if (ch == '-') f = -1;  // 遇到负号,设置符号为负
        ch = nextChar();        // 继续读下一个字符
    }
    
    // 读取整数部分
    while (ch >= 48 && ch <= 57) {
        x = x * 10 + (ch ^ 48);  // 整数部分累加(x*10 + 当前数字)
        ch = nextChar();         // 继续读下一个字符
    }
    
    // 若遇到小数点,处理小数部分
    if (ch == '.') {
        ch = nextChar();  // 跳过小数点,读取小数部分第一个字符
        // 读取小数部分,累加至y(y初始为0,每次乘10加当前数字)
        while (ch >= 48 && ch <= 57) {
            y = y * 10 + (ch ^ 48);
            mine++;       // 小数位数+1
            ch = nextChar();  // 继续读下一个字符
        }
    }
    
    precision = mine;  // 记录当前浮点数的小数位数(供后续输出使用)
    // 计算最终浮点数:符号 * (整数部分 + 小数部分/10^小数位数)
    return f * (x + y / power(10, mine));
}

// 快速读取布尔值函数:读取整数,非0为true,0为false
inline bool scan_bool() {
    int x = scan_int();  // 调用scan_int读取整数
    return x != 0;       // 整数非0返回true,否则返回false
}

// 快速读取字符函数:跳过空白字符(空格、换行、制表符等),读取第一个有效字符
inline char scan_char() {
    char ch = nextChar();  // 读取第一个字符
    // isspace(ch)判断是否为空白字符(空格、\n、\t等),跳过所有空白
    while (isspace(ch))
        ch = nextChar();
    return ch;  // 返回第一个非空白字符
}

// 输出缓冲区配置:BUF_SIZE为2^20字节(约1MB),用于批量写入输出数据
const int BUF_SIZE = 1 << 20;
char output_buffer[BUF_SIZE];  // 输出缓冲区数组
char *output_ptr = output_buffer;  // 输出缓冲区指针,指向当前写入位置

// 刷新输出缓冲区函数:将缓冲区中已写入的数据写入stdout,重置指针
inline void flush_output() {
    // fwrite:从output_buffer写数据到stdout,每次写1字节,共写(output_ptr - output_buffer)字节
    fwrite(output_buffer, 1, output_ptr - output_buffer, stdout);
    output_ptr = output_buffer;  // 指针重置到缓冲区起始位置,准备下次写入
}

// 快速输出整数函数:处理正负整数,写入输出缓冲区(比printf快)
inline void print_int(int x) {
    // 若x为负数,先写入负号'-',并将x转为正数
    if (x < 0) {
        *output_ptr++ = '-';  // 缓冲区指针后移,指向下次写入位置
        x = -x;
    }
    
    char temp[20];  // 临时数组:存储数字的逆序(因取模得到的是个位、十位...)
    int len = 0;    // 记录数字的位数,初始为0
    
    // 循环取模获取数字的每一位(从个位开始),存入temp
    do {
        temp[len++] = '0' + (x % 10);  // x%10得到个位,转为字符
        x /= 10;                       // x除以10,去掉个位
    } while (x);  // 直到x为0(do-while确保x=0时也会存入一个'0')
    
    // 倒序输出temp中的字符(从高位到低位),写入缓冲区
    while (len--)
        *output_ptr++ = temp[len];
    
    // 检查缓冲区是否即将满(预留20字节防止溢出),满则刷新
    if (output_ptr - output_buffer >= BUF_SIZE - 20)
        flush_output();
}

// 快速输出浮点数函数:按precision记录的精度格式化,写入输出缓冲区
inline void print_double(double x) {
    char temp[30];  // 临时数组:存储格式化后的浮点数字符串
    int len;        // 记录格式化后字符串的长度
    
    // 按精度格式化浮点数:
    // precision<0时,用"%.f"(无小数部分,四舍五入到整数)
    // 否则用"%.*f"(*表示精度由precision指定,保留precision位小数)
    if (precision < 0)
        len = snprintf(temp, sizeof(temp), "%.f", x);
    else
        len = snprintf(temp, sizeof(temp), "%.*f", precision, x);
    
    // 将格式化后的字符串写入输出缓冲区
    for (int i = 0; i < len; i++)
        *output_ptr++ = temp[i];
    
    // 检查缓冲区是否即将满,满则刷新
    if (output_ptr - output_buffer >= BUF_SIZE - 20)
        flush_output();
}

// 快速输出布尔值函数:输出"true"或"false"字符串到缓冲区
inline void print_bool(bool x) {
    // 若x为true,取"true"字符串;否则取"false"字符串
    const char *str = x ? "true" : "false";
    // 遍历字符串,逐个字符写入缓冲区
    while (*str) {
            *output_ptr++ = *str++;  // 将当前字符写入输出缓冲区,指针后移
    }
    // 检查缓冲区是否即将满(预留20字节防溢出),满则刷新
    if (output_ptr - output_buffer >= BUF_SIZE - 20)
        flush_output();
}

// 快速输出单个字符函数:将字符写入输出缓冲区,按需刷新
inline void print_char(char x) {
    *output_ptr++ = x;  // 直接将字符x写入缓冲区,缓冲区指针后移
    // 检查缓冲区是否即将满(预留20字节防溢出),满则刷新
    if (output_ptr - output_buffer >= BUF_SIZE - 20)
        flush_output();
}

// 主函数:示例用法——读取n个整数,计算它们的和并输出
int main() {
    // 1. 读取整数n:表示后续要读取的整数个数
    int n = scan_int();
    // 2. 定义sum变量:用于累加n个整数的和,初始化为0
    int sum = 0;
    
    // 3. 循环n次:每次读取一个整数,累加到sum中
    for (int i = 1; i <= n; i++) {
        sum += scan_int();  // 调用快速读入函数获取整数,加入总和
    }
    
    // 4. 输出结果:
    print_int(sum);       // 调用快速输出函数,将累加和写入输出缓冲区
    print_char('\n');     // 输出换行符,确保结果格式正确(单独占一行)
    flush_output();       // 强制刷新输出缓冲区:确保所有数据都写入stdout(避免缓冲区残留)
    
    return 0;  // 程序正常结束
}
posted @ 2025-09-14 10:33  九三青梧  阅读(24)  评论(0)    收藏  举报