快速读入详解
当你在信息学竞赛\((OI)\)中进入了提高组时,你可能会被卡常!
卡常!
程序被卡常数,一般指程序虽然渐进复杂度可以接受,但是由于实现/算法本身的时间常数因子较大,使得无法在OI/ICPC等算法竞赛规定的时限内运行结束。
常数被称为计算机算法竞赛之中最神奇的一类数字,主要特点集中于令人捉摸不透,有时候会让水平很高的选手迷之超时或者超空间。
——来自某度百科……
快速读入
简称快读,是信息学竞赛中卡常数最为常见的方法。
一般来讲,大多数题目的出题人都不会到这种丧心病狂的地步。
不过,以防万一肯定没坏处啊~ 反正代码很简单啦
代码
先上代码!讲解在后面。
inline int read(){
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
} // 这是能判负数的C++快读模板
在代码中,只需将cin >> n
或scanf("%d", &n)
改成n = read()
即可!
原理分析
为什么\(cin\)慢?因为它需要和\(stdio\)保持同步,也就是sync_with_stdio
。
为什么\(scanf\)慢?原因有点复杂。
- 它可以接受多种形式的输入(数字、字符串等等),因此需要判断。
- 它因为某些安全原因——输入太快可能会有些玄学的\(bug\),具体的我也不太清楚。
其实在\(C++\)中,依次读入单个字符是比一次读入一个数要快的,因此我们可以用\(getchar()\)来负责读入。
在实际的文件中,会有许多不必要的隐藏字符,比如换行符\n
等。
因此,我们需要先排除掉这些字符,也就是第一个\(while\)循环。但是有一个特例:\(-21904\)中的\(-\)号。这个负号不是数字啊!于是我们用\(w\)当数的符号。有负号时,\(w\)从原来的\(1\)转化成了\(-1\)。
于是,我们要特判!if(ch == ‘-’) w = -1;
这就是判负号的语句。
下一个循环中,就是位值原理。数\(\overline{abcd} = 10 \times (10 \times (10 \times a + b) + c) + d\),读者自证不难。
最后返回\(n = sgn(n) \times |n|\),其中\(sgn(x)\)为\(x\)的符号。