输入输出
1.cin/cout关闭同步流
cin 和 cout效率低的原因是,c++默认 cin`与stdin保持同步,cin会把要输出的东西先存入缓冲区,进而消耗时间。如果我们关闭了同步,cin/cout也会变得很快。
注意:关闭同步流后一定不要使用printf scanf,pus,getchar等其它输入输出流,会出错的!
关闭同步流代码
1 2
| ios::sync_with_stdio(false),ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0)
|
2. 换行不要用endl
c++里,每次使用endl
都会清空缓冲区,这会浪费很多时间,建议使用'\n'
的方式。
解决方案
3. 换用stdio
在某些评测机上,即使关闭了同步流,也没有printf
和scanf
快,所以还是养成C风格输入输出的好习惯吧!
1 2
| printf("%d\n",a); scanf("%d",&a);
|
4. 快读快写
在输入输出的领域里,快读快写才是王者!
以下是我常用的快读快写板子。
快读
1 2 3 4 5 6 7 8 9 10 11
| inline int read(){ int x=0,y=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') y=-1,c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<1)+(x<<3)+(c^48),c=getchar(); } return x*y; }
|
快写
1 2 3 4 5 6 7
| void write(int x){ if(x<0) putchar('-'),x=-x,write(x); else{ if(x>9) qwrite(x/10); putchar(x%10+'0'); } }
|
编译器优化
5. 使用inline
inline叫做内联函数,在c++里有些函数经常调用且不复杂,在这种函数前加上inline,作用相当于宏定义。
如
1 2 3
| inline int sum(int a,int b){ return a+b; }
|
相当于
1
| #define sum((a),(b)) (a)+(b)
|
注意:对于一些复杂函数(尤其是递归函数),inline没有一点优化作用(编译器不会给你优化)
6. O2优化
这个绝对是耳熟能详的了,O2优化能减少运行时间,尤其是对STL的优化非常大。
把这行代码放到程序最前面即可。
7. 火车头优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #pragma GCC optimize(3) #pragma GCC target("avx") #pragma GCC optimize("Ofast") #pragma GCC optimize("inline") #pragma GCC optimize("-fgcse") #pragma GCC optimize("-fgcse-lm") #pragma GCC optimize("-fipa-sra") #pragma GCC optimize("-ftree-pre") #pragma GCC optimize("-ftree-vrp") #pragma GCC optimize("-fpeephole2") #pragma GCC optimize("-ffast-math") #pragma GCC optimize("-fsched-spec") #pragma GCC optimize("unroll-loops") #pragma GCC optimize("-falign-jumps") #pragma GCC optimize("-falign-loops") #pragma GCC optimize("-falign-labels") #pragma GCC optimize("-fdevirtualize") #pragma GCC optimize("-fcaller-saves") #pragma GCC optimize("-fcrossjumping") #pragma GCC optimize("-fthread-jumps") #pragma GCC optimize("-funroll-loops") #pragma GCC optimize("-fwhole-program") #pragma GCC optimize("-freorder-blocks") #pragma GCC optimize("-fschedule-insns") #pragma GCC optimize("inline-functions") #pragma GCC optimize("-ftree-tail-merge") #pragma GCC optimize("-fschedule-insns2") #pragma GCC optimize("-fstrict-aliasing") #pragma GCC optimize("-fstrict-overflow") #pragma GCC optimize("-falign-functions") #pragma GCC optimize("-fcse-skip-blocks") #pragma GCC optimize("-fcse-follow-jumps") #pragma GCC optimize("-fsched-interblock") #pragma GCC optimize("-fpartial-inlining") #pragma GCC optimize("no-stack-protector") #pragma GCC optimize("-freorder-functions") #pragma GCC optimize("-findirect-inlining") #pragma GCC optimize("-fhoist-adjacent-loads") #pragma GCC optimize("-frerun-cse-after-loop") #pragma GCC optimize("inline-small-functions") #pragma GCC optimize("-finline-small-functions") #pragma GCC optimize("-ftree-switch-conversion") #pragma GCC optimize("-foptimize-sibling-calls") #pragma GCC optimize("-fexpensive-optimizations") #pragma GCC optimize("-funsafe-loop-optimizations") #pragma GCC optimize("inline-functions-called-once") #pragma GCC optimize("-fdelete-null-pointer-checks") #pragma GCC optimize(2)
|
8. 宏定义
宏定义就是在编译时把代码中的片段替换,如
就是把程序中所有的main换成mian。
宏定义一般用来缩短代码量,如
1
| #define FR(i,l,r) for(int i=(l);i<=(r);i++)
|
如果有一个几重的for循环嵌套,用宏定义就会减少很多代码量。
宏定义有时用来偷懒,如
这行代码会把程序中所有int换成long long,但是也会把int main()
换成long long main()
所以我们需要改动一下。
1 2 3
| signed main(){ return 0; }
|
这样就不会CE了。
其它优化(不会归类了)
9. 使用register
register表示把变量放在寄存器里,一般用于变量经常使用的情况下(如循环变量)
实例
1
| for(register int i=1;i<=n;i++)
|
如果程序里循环特别多,有时能优化三分之一的时间
10. 直接赋值
正常的int a=1
是先定义变量a,再给a赋值1。而int a(1)
这种写法是在定义变量时就把1放进变量里了,所以会快一些。
注意,这种写法只能在定义时使用。
11. 三目运算符
三目运算符通常比ifelse要快(我也不知道为什么),而且代码也短一些。
如
1 2 3 4
| int max(int a,int b){ if(a>b) return a; return b; }
|
用三目运算符就会写成
1 2 3
| int max(int a,int b){ return a>b?a:b; }
|
12. 不用库函数
众所周知,c++给的库函数都很慢,有时为了极限卡常就会自己写一些简单的库函数。
如取最大值
1 2 3
| inline int myMax(int a,int b){ return b&((a-b)>>31)|a&(~(a-b)>>31); }
|
取模
1 2 3
| inline int myMod(int a,int b){ return a-(a/b)*b; }
|
13. 位运算
关于乘除2的操作一般用左移右移代替。
如线段树中的
1
| tr[x].sum=tr[x*2].sum+tr[x*2+1].sum;
|
可以写成
1
| tr[x].sum=tr[x<<1].sum+tr[x<<1|1].sum;
|
如线段树开四倍空间一般直接写成
14. 一些语法糖
以下的语法糖大多是c++11或c++14中出现的,请放心使用(ccf让用)
结构体赋值
假如有一个结构体
1 2 3 4
| struct node{ int a,b,c; }; node d;
|
我们可以直接这样赋值
auto妙用
auto可以自动推断类型,一些难写的stl容器的迭代器都可以用auto代替
1
| for(auto it=v.begin();it!=v.end();it++);
|
比如很难写的set迭代器就可以这样用:
1 2
| auto it=s.lower_bound(k); cout<<*(--it);
|
甚至auto可以这样用
1 2 3
| for(auto it:v) cout<<it<<endl;
|
不是stl容器也可以这么用,甚至是
1
| for(auto it:{&x,&y,&z}) cout<<*it<<" ";
|
大括号传参
从@qidirj那里听到的有趣语法。
如要取x,y,z三数中的最大值,正常是这样写的:
在c++14后可以这样写
很神奇……