市面上的快读和快写,大致过程是手动扩大缓冲区,并手动将数字转化为字符,使用 fread / fwrite
进行最终缓冲区的输入和输出。
考虑阅读 std::setvbuf
的文档(link),发现这东西可以设置自己的缓冲区,为自己所用,同时可以设置大小。由此,我们不难写出如下的神秘代码:
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
constexpr size_t mybufsize = 1 << 20;
char myinbuf[mybufsize], myoutbuf[mybufsize];
int main() {
setvbuf(stdin, myinbuf, _IOFBF, mybufsize);
setvbuf(stdout, myoutbuf, _IOFBF, mybufsize);
unsigned long long ans = 0, x;
for (int i = 0; i < 3e6; i++) {
scanf("%llu", &x);
ans ^= x;
}
printf("%llu\n", ans);
return 0;
}
这段代码首先篡改了 stdio
和 stdout
的缓冲区并使其大小扩大到 1 << 20
。然后进行正常的 scanf / printf
输入输出。在 loj.ac/p/7 上跑到 900 ms。与平常手写的快读速度慢一点点。
发现 scanf
解析 const char* fmt
的过程本身也有常数,考虑更换为关闭与 stdio
同步的 cin / cout
,可以跑到 700 ms:
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
typedef long long LL;
constexpr size_t mybufsize = 1 << 21;
char myinbuf[mybufsize], myoutbuf[mybufsize];
int main() {
setvbuf(stdin, myinbuf, _IOFBF, mybufsize);
setvbuf(stdout, myoutbuf, _IOFBF, mybufsize);
cin.tie(nullptr) -> sync_with_stdio(false);
unsigned long long ans = 0, x;
for (int i = 0; i < 3e6; i++) {
cin >> x;
ans ^= x;
}
cout << ans << endl;
return 0;
}
运行时间 788 ms。于是你获得一个和手写快读速度差不多的快读。
请注意,你可能需要先进行 freopen
再进行 setvbuf
,因为
此函数仅可在已将 stream 关联到打开的文件后,但要在任何其他操作(除了对 std::setbuf/std::setvbuf 的失败调用)前使用。