作为一个专业 fake快读/快写/压行/宏定义/类/人,今天就来给大家讲一些有趣的小技巧 因为太菜了所以大家多多包涵 因为太菜了,所以像ahy、dms这样的大佬就看个笑话就行了呜呜呜
快读&快写
在数据量超过1e6的时候,快读快的不是一星半点——笔者自己说的
快读
众所周知,常用的读入方式有三种: 1.cin 2.scanf 3.getchar
没错,你现在一定很懵:什么?说好的快速读入呢? 别着急,听我细细道来 首先呢我们回忆回忆呗,当你刚接触oi的时候,你觉得最好用的读入是什么呢 我觉得大多数人都会觉得cin很好用吧,写起来简单,不需要考虑格式化输入都是cin的优点 但是在教练的强制禁令之下,你停止了使用cin,改用scanf了,但是你不知道为什么 你可能只觉得:WA!scanf好麻烦啊,内个什么文本替代我根本记不住啊,还不如cin呢,也许有人偶尔还在偷偷用着cin 直到有一天,你做着一道数据稍稍大了一点点的题,你觉得没什么问题,你提交了,然后就是 TLE 但是你并不知道为什么,但是当你重构的时候一不小心写成了scanf或者是看题解写了scanf你就借鉴了,总之你惊奇地发现cin读入速度竟然慢于scanf,于是为了AC,你就走上了坚定不移的scanf之路,但是走着走着,数据来到了1e6以上,你发现有的时候scanf都满足不了你了,甚至有时还有奇怪的输入,scanf不好用了! 你迫切地需要一款更快的读入! 于是我们今天的主要话题应运而生: 如何加快读入 在进入正式介绍快读之前,首先有个小彩蛋,就是cin是可以优化的,你只要加上这句:
ios::sync_with_stdio(false);
cin,cout之所以效率低,是因为先把要输出的东西存入缓冲区,再输出,导致效率降低,而这段语句可以来打消iostream的输入输出缓存,可以节省许多时间,使效率与scanf与printf相差无几,但是还是比scanf与printf略慢,但已经不影响题目通过了。 这个函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起。 所以使用ios::sync_with_stdio(false);后就不能把cin、cout和scanf、printf混用了,否则有一定几率会发生输入输出和预想不一样的情况。 所以还是不要用cin、cout了 那么现在我们来介绍一个更快的读入方式吧 当遇到相当大的数据的时候(一般1e6以上),或者我们用其他方式卡常已经不能再快了,这时我们可以使用快读 快读原理是什么呢? 显然,读入字符可比读入数字快多了,那么我们就按字符挨个读入,读入后用ASCII码处理成数字,这样就很快了 最基本的快读就像这样↓
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
不过快读也有可能被卡,比如一个数据有很多很多空格 笔者还没有见过如此毒瘤的数据( 你觉得这就完了吗? 而实际上,甚至还有更快的读入方式:fread 这个函数的原理就是先把数据流中的一整段都存下来,然后从这个数组里读取,直到数组读空了再重新从数据流中读取,由于是整段整段读取,所以自然比getchar()要快的多。 这两个东西在各种OJ上面还是适用的,因为OJ上就是文件读入输出的。 不过我们会发现,一旦用了这个读入优化。getchar,scanf都不能用了(存到buf里了),所有读入都必须自己写了。所以说数据流不是太大的时候(如1*10^6),可以考虑不用这个读入优化。 代码长这样↓
static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
四种读入方式分别的时间 测试用题为st表模板 基本忽略因评测机原因的上下浮动 控制单一变量为读入方式 自下而上为cin(甚至T了,垃圾)→scanf→读入优化→fread读入优化 四种方式各有优缺点,下面我们就来总结一下
| 非常方便 | 方便 | 多种读入类型 | 能过 | 快 | 非常快 | 终端调试 |
---|
cin | √ | | √ | × | | | √ | scanf | | √ | √ | √ | | | √ | 读入优化 | | | | √ | √ | | √ | fread | | | | √ | | √ | × |
最后就是笔者在使用不同读入时总结的一些问题、经验和教训 cin:没什么可说的,因为太垃圾笔者没用过 scanf: 1.使用scanf的时候最容易犯的也是最常见的问题就是没打取地址符,这个问题是不会CE,只会RE,但是一定要注意!scanf输入字符串本来也没有取地址符! 2.少一个或者多一个%d也是很常见的问题,多一个%d会因为找不到地址RE,少一个%d你的变量就会少值 3.文本替代符写错类型也是一个常见问题,尤其是%lld和%d,一定要确定好读入类型再写文本替代符 读入优化: 1.读入优化最最最最最容易出现的问题就是类型,虽然只能是int或者ll,但是一定要看好读入的的变量将会是什么类型,这里千万不能写错,ll写成int你题就炸了,int写成ll可能连赋值都做不到 2.只能用于整数,如果有什么浮点数建议直接放弃 3.写的时候一定要注意好别写错了,比如少个getchar之类的,如果没了会直接TLE或者RE fread: 1.同读入优化 2.只能使用文件读入
快写
内容基本与快读一致,此处将给出代码但不予赘述,请读者自行理解 快速读入唯一的问题是不能输出0,这里需要自行特判 以及fwrite可以在终端输入的情况下使用,但是会直接输出为文件形式 原初の快速输出↓
inline void write(int x){
if(x==0){putchar('0');return;}
int len=0,k1=x,c[10005];
if(k1<0)k1=-k1,putchar('-');
while(k1)c[len++]=k1%10+'0',k1/=10;
while(len--)putchar(c[len]);
}
fwrite快速输出↓
static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf;
#define putchar(x) (p3-obuf<1000000)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
inline void write(int x){
if(x==0){putchar('0');return;}
int len=0,k1=x,c[10005];
if(k1<0)k1=-k1,putchar('-');
while(k1)c[len++]=k1%10+'0',k1/=10;
while(len--)putchar(c[len]);
}
那么我们有关快读快写的一些主要内容就结束了,下面是
一些私货神奇技巧
关于高亮
笔者个人比较喜欢对比度较高的,颜色比较鲜亮的高亮主题,比如Matrix、Just Black,有的时候一个对比度高的高亮也许能救你一命!
关于压行
笔者觉得将代码压行是一种艺术,紧凑的代码风格简直是美极了,将同一功能的句落压到一行有助于debug,没有必要展开的if和for就写到一行就完了,比如笔者本人曾经将树剖板子写到了优美的90行
关于宏定义
相较于typedef和const int,笔者比较喜欢整齐划一的define,不但方便写一些冗长的语句,而且宏定义有的时候能有效地减少变量的数量,降低程序的空间常数
关于类
类是非常有用的东西,毕竟是C++三大特性之一,你可以利用类轻松地将一些很冗长的和数据结构有关的或者实现某种指定功能的代码(比如树剖)封装到一个类里面,然后你就可以轻松的像使用STL一样使用它了,笔者很喜欢将什么线段树st表并查集图树矩阵之类的东西存到类里
这基本上就是全部内容了,不喜勿喷,欢迎补充
|