#关于CPP算法竞赛中的快读、快写
作者:成都理工大学 胡鑫
前言
一些事实
-
在C++中,常见的读写速度
std::cin std::cout
<scanf() printf()
<getchar() putchar()
<fread() fwrite()
。(当然 加速 之后的std::cin std::cout
的速度与scanf() printf()
不相上下) -
大多数的OJ都能通过
fread() fwrite()
读写数据。
平时在刷题或比赛的时候,会出现一些题,虽然写出了正解,但仍会卡掉你的某一两个测试点,然后用快读快写之后,就AC
了。
为什么快读会这么快?
因为读入输出字符比读入输出数字快许多,所以这里介绍基于fread() fwrite()
的快读、快写方法及实现。
快读的实现
如何用fread()
来getchar()
?
fread函数
(参考https://cplusplus.com/reference/cstdio/fread/)
size_t fread(void *restrict buffer, size_t size, size_t count, FILE *restrict stream);
其中buffer
是读取数据的字符串,size
是一次读入的个数,count
是读入的次数,stream
是输入流。
为了接收fread()
函数的输入,我们定义了:
char buffer[0xfffff], *front = buffer, *back = buffer;
由此重写getchar()
函数:
char getchar() {
if (front == back) {
back = buffer + fread(buffer, 1, 0xfffff, stdin);
front = buffer;
}
return *(front++);
}
用重写的getchar()
读入一个整型
以' '
,'\n'
为分割,按位读取一个整数:
void read(int &x) {
char c;
do {
c = getchar();
} while (c == ' ' || c == '\n');
x = 0;
int w = 1;
if (c == '-') {
w = -1;
c = getchar();
}
do {
x = (x << 1) + (x << 3) + c - '0';
c = getchar();
} while (c != ' ' && c != '\n');
x *= w;
}
这样,就完成了快读操作的实现。
快写的实现
如何用fwrite
来putchar()
?
fwrite函数
(参考https://en.cppreference.com/w/c/io/fwrite)
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream);
与fwrite()
相同,buffer
是读取数据的字符串,size
是一次读入的个数,count
是读入的次数,stream
是输入流。不过不同的是,我们可以一次性输出所有的数据。
于是我们定义:
char *top, buffer[0xfffff];
由此我们又可以重写putchar()
函数:
void putchar(char x) {
if (top - buffer < 0xfffff) {
*top++ = x;
} else {
fwrite(buffer, top - buffer, 1, stdout);
top = buffer;
*top++ = x;
}
}
当然,每执行一次函数就会判断一次top - buffer < 0xfffff
,所以我们手动定义一个刷新函数void flush()
,然后在缓冲区满和程序结束后刷新。
void flush() {
fwrite(buffer, top - buffer, 1, stdout);
top = buffer;
}
用重写的putchar()
来输出一个整型
void print(const int &x) {
if (x > 9) {
print(x / 10);
}
putchar(x % 10 + '0');
}
由此我们完成了快写的实现。
快读快写的封装类
既然我们已经完成了快读,快写的实现,那我们不妨效仿我们熟悉的std::cin std::out
对<<
,>>
符号的重载,将上述的快读快写封装为一个C++类。
代码如下:
namespace iof {
const int Buffer_MAXSIZE = 0xfffff;
const char endl = '\n';
class ifast {
public:
ifast &operator>>(char &ch);
ifast &operator>>(char *s);
ifast &operator>>(std::string &string);
ifast &operator>>(bool &x) { return read_unsigned_integer(x); }
ifast &operator>>(int &x) { return read_signed_integer(x); }
ifast &operator>>(long long &x) { return read_signed_integer(x); }
ifast &operator>>(unsigned int &x) { return read_unsigned_integer(x); }
ifast &operator>>(unsigned long long &x) { return read_unsigned_integer(x); }
void getline(std::string &string);
void getline(char *s);
auto hasMoreToken() { return front <= back; }
private:
template<typename T>
ifast &read_signed_integer(T &x);
template<typename T>
ifast &read_unsigned_integer(T &x);
char getchar();
char buffer[Buffer_MAXSIZE]{};
char *front = buffer;
char *back = buffer;
char next_char{};
} fin;
class ofast {
public:
ofast &operator<<(const char &c);
ofast &operator<<(const char *c);
ofast &operator<<(const std::string &s);
ofast &operator<<(const bool &x) { return print_integer(x); }
ofast &operator<<(const int &x) { return print_integer(x); }
ofast &operator<<(const long long &x) { return print_integer(x); }
ofast &operator<<(const unsigned int &x) { return print_integer(x); }
ofast &operator<<(const unsigned long long &x) { return print_integer(x); }
void flush();
~ofast() { flush(); }
private:
void putchar(const char &c) { *top++ = c; }
char buffer[Buffer_MAXSIZE]{};
char *top = buffer;
template<typename T>
ofast &print_positive_integer(const T &x);
template<typename T>
ofast &print_integer(const T &x);
} fout;
char ifast::getchar() {
if (front == back) {
back = buffer + fread(buffer, 1, Buffer_MAXSIZE, stdin);
front = buffer;
}
return *(front++);
}
void ifast::getline(std::string &string) {
string.clear();
for (*this >> next_char; hasMoreToken() && next_char != '\n'; next_char = getchar()) {
string.push_back(next_char);
}
}
void ifast::getline(char *s) {
for (char *p = s; hasMoreToken() && next_char != '\n'; next_char = getchar(), p++) {
*p = next_char;
}
}
ifast &ifast::operator>>(char &ch) {
do {
ch = getchar();
} while (hasMoreToken() && (ch == ' ' || ch == '\n'));
return *this;
}
ifast &ifast::operator>>(char *s) {
*this >> next_char;
for (char *p = s; hasMoreToken() && next_char != ' ' && next_char != '\n'; next_char = getchar(), p++) {
*p = next_char;
}
return *this;
}
ifast &ifast::operator>>(std::string &string) {
*this >> next_char;
string.clear();
for (; hasMoreToken() && next_char != ' ' && next_char != '\n'; next_char = getchar()) {
string.push_back(next_char);
}
return *this;
}
template<typename T>
ifast &ifast::read_unsigned_integer(T &x) {
*this >> next_char;
x = 0;
do {
x = (x << 1) + (x << 3) + next_char - '0';
next_char = getchar();
} while (hasMoreToken() && next_char != ' ' && next_char != '\n');
return *this;
}
template<typename T>
ifast &ifast::read_signed_integer(T &x) {
*this >> next_char;
auto isNegative = next_char == '-';
if (isNegative) {
next_char = getchar();
}
x = 0;
do {
x = (x << 1) + (x << 3) + next_char - '0';
next_char = getchar();
} while (hasMoreToken() && next_char != ' ' && next_char != '\n');
if (isNegative) {
x = -x;
}
return *this;
}
ofast &ofast::operator<<(const char &c) {
putchar(c);
return *this;
}
ofast &ofast::operator<<(const char *c) {
for (auto *p = c; *p != '\0'; p++) {
putchar(*p);
}
return *this;
}
ofast &ofast::operator<<(const std::string &s) {
for (const auto &i: s) {
putchar(i);
}
return *this;
}
void ofast::flush() {
fwrite(buffer, 1, top - buffer, stdout);
top = buffer;
}
template<typename T>
ofast &ofast::print_integer(const T &x) {
if (x > 0) {
return print_positive_integer(x);
} else if (x == 0) {
putchar('0');
return *this;
} else {
putchar('-');
return print_positive_integer(-x);
}
}
template<typename T>
ofast &ofast::print_positive_integer(const T &x) {
if (x > 9) {
print_positive_integer(x / 10);
}
putchar(x % 10 + '0');
return *this;
}
}
注意事项
-
ifast, ofast
不能用于读写浮点数 -
该快读类无法与其他的输入输出类型同时使用
-
ofast
的缓冲上限是0xfffff
,如果输出过长需要手动刷新
实际应用
以洛谷P9027为例:
#include <iostream>
#include <cmath>
//...
using namespace std;
using namespace iof;
int n, m;
int a[200010], stf[200010][20], q[200010][3], f[200010][20];;
int gcd(int x, int y) {
return y ? gcd(y, x % y) : x;
}
int lcm(int x, int y) {
return x * y / gcd(x, y);
}
int query(int l, int r) {
int k = log2(r - l + 1);
return gcd(stf[l][k], stf[r - (1 << k) + 1][k]);
}
signed main() {
fin >> n >> m;
for (int i = 1; i <= m; i++) {
int l, r, x;
fin >> l >> r >> x;
q[i][0] = l;
q[i][1] = r;
q[i][2] = x;
f[l][x]++, f[r + 1][x]--;
}
for (int i = 1; i <= n; i++) {
int k = 1;
for (int j = 1; j <= 16; j++) {
f[i][j] += f[i - 1][j];
if (f[i][j]) k = lcm(k, j);
}
a[i] = k;
}
for (int len = 0; len < 20; len++) {
for (int i = 1; i + (1 << len) - 1 <= n; i++) {
if (!len) {
stf[i][len] = a[i];
} else {
stf[i][len] = gcd(stf[i][len - 1], stf[i + (1 << (len - 1))][len - 1]);
}
}
}
for (int i = 1; i <= m; i++) {
if (query(q[i][0], q[i][1]) != q[i][2]) {
fout << "Impossible";
return 0;
}
}
for (int i = 1; i <= n; i++) {
fout << a[i] << ' ';
}
return 0;
}
这是io加速后的std::cin std::cout
的速度:
而用了快读的速度:
可以看到明显快了20%-30%的速度。
后记
如有不足,请大佬在评论区指出!