F小蒟蒻教你卡常

别看标题,那都是假的

以下的玩意儿转自:

1.CSDN dalao *1

2.CNBLOGS dalao *1

3.

···


DALAO 1

如果编译器没有开O2优化 
用库函数常数会凭空增加很多。。 
似乎NOIP考场不开O2 
某些时候,如果你优化到无法再优化的时候 
尝试去自己重新实现库函数。

比如

  • isdigit()
  • max()/min()
  • unique()/lower_bound()/upper_bound()
  • scanf()/printf()
  • cin/cout
  • getchar()/putchar()
  • STL::queue/stack/priority_queue/deque

常数优化:

位运算

没有O2的时候(有O2不用管。编译器会帮你) 
x*10 <=> (x<<3)+(x<<1) 
x!=y <=> x^y 
x!=-1 <=> ~x 
x*2 <=> x<<1 
x*2+1 <=> x<<1|1 
x/2 <=> x>>1 
(x+1)%2 <=> x^1 
x%2 <=> x&1 
x%2==0 <=> ~(x&1)

语法

inline 在非递归函数前加修饰。 
循环变量 int i =>register int i

c++没有尾递归优化。所以可以自己手写栈来优化递归。

原则上尽量减少乘/除/取模 指令 
取模指令如果是逐渐累加的话,

x+=add;x%=mod; 
=> x+=add;x>=mod?x%=mod:1;

A?B:C 好像要比if,else语句快。

memset初始化细节 memset(a,0x3f,sizeof(a)); 
最后的a[1] = 0x3f3f3f3f 
int的极限是 0x7fffffff 
还可以~0u 
INF有的时候不要刚好赋值到0X7FFFFFFF,如果有2个inf的值相加就会溢出。

乘法溢出。 
这个要注意。不要直接全部long long这样慢很多

关于类型转换,一般不用管,编译器会处理。 
但是,考试环境有点老?没试过,所以如果有不同类型的话最好在前面显示的强转一下。

赋值>int的话请在数字后加LL

【Update1】2016-11-13

下面的不是绝对,环境不同可能不会出错。 
建议平时在编译的时候把编译指令加上 “-ansi”

信息学竞赛的一些注意事项:

  • pow()函数请慎用,低版本有的时候会CE。
  • 考场不允许使用“bits/stdc++.h”库,并且使用该库变量名可能不能使用next (C++库里面有个template是next会CE)
  • 请尽力少用黑语法。
  • 二分图匹配避免link做变量名(还有个什么变量名Linux也会CE我突然记不到了..有时其实也可以用“中国式的变量名命名法”这样不会CE。 不推荐这种诡异的风格),Linux环境可能会CE。
  • 少用“math.h”|“cmath”库。因为_x,_y,y1,y2,x1,x2,x0,y0,这类命名有时会CE。
  • 考场严禁使用带下划线的库函数。eg. __gcd()
  • 编程时利用宏可以减少代码量,但是请务必在每个变量里加括号。 eg. #define rep(i,s,t) for(int i=(s);i<=(t);i++)
  • 循环变量for(int i;…;…;)请不要放到全局上。这种常数不会卡。相反会带来很多隐式的错误
  • 如果你不精通指针请少用它。指针的代码很难查错。竞赛里面请避免使用函数指针,多级指针,指针数组这样的语法。
  • 如果可以静态实现,请先考虑静态版本的代码。而不是写动态。(malloc() new
  • 引用和指针不是一个东西。这个语法我已经不想解释了。去买本语法书细读。
  • 考试少用C++的OOP特性,可以使用STL template<> class namespace 但不推荐使用。
  • 请熟悉STL里面的 string queue stack vector set map 后面这些用的少,仅供参考并且在pascal选手消失前应该是不会考的前面这些只是方便才用,但请注意常数!推荐自己实现。 deque multiset multimap bitset
  • 宏指令少用,#progma 肯定是禁了的,别想手动扩栈。涉及操作编译器和系统的函数都要挂。
  • 内嵌汇编也是算作弊处理,毕竟这是算法竞赛,不是信息安全竞赛,也不是编程能力竞赛。

下面的话来自一位编程大神有可能我记错了或者大神说错了.. 
然后有的童鞋认为memset()既然这么容易错,那为什么我们还要用呢,直接for一遍初始化。请注意,memset()底层是用汇编实现的效率要比直接的快4倍,不是所有的库函数都是c\c++实现的。 
(我记错了吗..还是strlen() 记不到了QAQ,但是memset实现应该不是直接for,肯定有很多常数优化和位运算) 
哦,然后就是strlen() 重点! 像下面这种代码复杂度是o(nL)的,L为str的长度。 
for(int i=0;i<strlen(str);i++ 
已经有很多人还是写的上面的这种代码却一直不知情。等你被卡了就知道了。

最后还有一个问题。由于没有开O2优化,会导致一些本来没有区别的变得比较明显。多维数组请把大的放前面。eg. int dp[10000][10][2] 而不是 dp[2][10][10000],常数差距0.5s。比算法的差距还大。开了o2后差别不明显。

还有一个坑。那些将cin/cout和scanf()/printf()一起用的朋友们,如果你们的代码再怎么查都查不出错了,这个时候要考虑是不是把c++和c的IO函数混用了。这个会导致一些潜在性的问题。尤其是当你用了ios::sync_with_stdio(false); 后。

代码风格

初学者。 
示例:

1 #include<cstdio>
2 using namespace std;
3 
4 int main()
5 {
6     //Do something you want but please make sure it is right.
7     return 0;
8 }
stdone

上下括号请对齐。请保持缩进。

1 for(int i=1;i<=n;i++)
2 {
3    for(int j=1;j<=n;j++)
4    {
5      //Do something who can make you happy.(滑稽)
6    }
7 }
stdtwo

变量名函数名推荐按照 驼峰命名法 eg. checkOfInput() 
函数名,变量名最好不要用没有意义的名字。比如,你要检查素数,函数名更好是checkPrime() or isPrime() 这类的,而不是solve() 
f() 当然也可以直接check() 但是当你有多个函数的时候为了不让自己混淆请使用最前面的方法。 
还有变量名,比如说你要写动态规划,状态数组最好开成dp[][].. 
这是大家约定俗成的。这样方便大家互相阅读。也方便别人帮你查错。 
还有一些约定俗成的:

 1 dfs()//deep-first-search
 2 bfs()//bread-first-search
 3 maxflow(),dinic()等//最大流
 4 isprime(),getprime()//检查素数,筛素数
 5 getdis()//计算欧几里德距离,曼哈顿距离
 6 query()//查询操作
 7 queryMax()/querySum()
 8 update()//更新操作
 9 tarjan()//有多种tarjan..找强联通分量/双联通分量/LCA的tarjan算法。
10 LCA()、RMQ()//字面意思..
11 check()//一般是二分的check()函数
12 solve()//字面意思..
13 match()//二分图匹配..
14 gethash()//字面意思..
15 getid()//字面意思..
16 getrank()//字面意思..
17 sort()//字面意思..
18 pre()//预处理
19 
20 dp[][] 一般是dp状态定义 或者f[][]/g[][]
21 dfn[] dfs序 que[]/q[]/sta[]/s[] 手写栈/队列 head,tail维护首尾。
22 //
23 一般意义下: M->边 N->点 Q->操作数
24 struct Edge{
25     int to,next,w;
26 }e[M]
27 struct Edge{
28     int u,v,w;
29 }e[M]
30 #define maxn ..
31 #define N ..
32 #define M ...
33 #define mod ...
34 #define max3(a,b,c) max(a,max(b,c))
35 #define isdigit(x) (x>='0'&&x<='9')
36 #define lson u<<1
37 #define rson u<<1|1
stdthree

代码中插入适当的空格

1 for(int i = 1; i <= n; i++)
2 x = (a + b) / 2
3 ans = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
stdfour

当然上面有的地方空格可以略去,看个人习惯。

1 for(int i=1;i<=n;i++)
2 for(int i = 1;i <= n; i++)
3 for(int i = 1; i < = n ; i++)
4 for(int i = 1; i <= n ; i++)
5 for(int i=1;i <= n;i++)
6 
7 #define rep(i,s,t) for(int i=(s);i<=(t);i++)
8 rep(i,1,n)
stdfive

请不要在一行做过多的事。

 1 for(int i = 1; i <= n ; i++)scanf("%d",&a[i]),a[i]<0?a[i]=-a[i]:1;
 2 
 3 //别那样↑
 4 //==============
 5 //应该这样↓
 6 
 7 
 8 for(int i = 1; i <= n ; i++)
 9     scanf("%d",&a[i]),a[i]<0?a[i]=-a[i]:1;
10 
11 
12 for(int i = 1; i <= n ; i++){
13     scanf("%d",&a[i]);
14     a[i]<0?a[i]=-a[i]:1;
15 }
16 for(int i = 1; i <= n ; i++)
17 {
18     scanf("%d",&a[i]);
19     if(a[i]<0)a[i] = -a[i];
20 }
stdsix

如果同样的计算要出现3次以上请写成函数。 
最简单的例子是getdis(),abs()

这样做的好处是方便调试。

代码压行

这个是比较有争议的。代码风格好的可以跳过了。刚学的话请别这么干。否则你遇见bug后会放弃治疗。

为什么要压行?

时间短,浪费在代码风格上无意义。 
所以。与上面对立的过程。

 1 for(int i=1;i<=n;i++)
 2 {
 3     //do sth..
 4 }
 5 
 6 ========================
 7 for(int i=1;i<=n;i++{
 8     //do sth..
 9 }
10 ========================
11 #define rep(i,s,t) for(int i=(s);i<=(t);i++)
12 rep(i,1,n){
13     //do sth..
14 }
15 =========================
16 #define rep(i,t) for(int i=1;i<=(t);i++)
17 rep(i,t){/*do sth..*/}
18 
19 ===========================
20 ===========================
21 for(int i=head[u];~i;i=e[i].next){
22     //do sth..
23 }
24 
25 ==============================
26 #define each(x) for(int i=head[x];~i;i=e[i].next)
27 each(u){ /*do sth ..*/}
28 
29 ================================
30 ================================
31 int gcd(int a,int b)
32 {
33     if(!b)return a;
34     else return gcd(b,a%b);
35 }
36 
37 ================================
38 int gcd(int a,int b){return !b?a:gcd(b,a%b);}
39 ================================
40 int gcd(int a,int b)
41 {
42     int t;
43     while(b!=0)
44     {
45         t = a;
46         a = b;
47         b = t%b;
48     }
49 }
50 ===============================
51 int gcd(int a,int b){for(int t;b!=0;t=a,a=b,b=t%b);}
52 ===============================
stdseven

 


 

DALAO 2

    1. IO优化
      • fread 和 fwrite ,如果还想再优化有mmap....(然而并不会用,好像也没用。。。)
      • 读入优化(这个非常重要!!!!!!!)
      1 inline int Read()
      2 {
      3     int x=0,f=1;char c=getchar();
      4     while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
      5     while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
      6     return x*f;
      7 }
      stdone
      • 输出优化好像用不到唉( ˇˍˇ )
    2. inline
      在声明函数之前写上inline修饰符(就像上面Read()一样),可以加快一下函数调用,但只能用于一些操作简单的函数。涉及递归,大号的循环等很复杂的函数,编译器会自动忽略inline。

    3. register
      在定义变量前写上register修饰符,用于把变量放到CPU寄存器中,适用于一些使用频繁的变量:

      1 register int n,m;
      stdtwo

      寄存器空间有限,如果放得变量太多,多余变量就会被放到一般内存中;
      快,不是一般的快,快到什么程度呢?:

      1 register int a=0;
      2 for(register int i=1;i<=999999999;i++)
      3 a++;
      4 int a=0;
      5 for(int i=1;i<=999999999;i++)
      6 a++;
      stdthree

      结果:
      优化:0.2826 second
      不优化:1.944 second
      恐怖啊!!!!

    4. 循环展开

      循环展开也许只是表面,在缓存和寄存器允许的情况下一条语句内大量的展开运算会刺激 CPU 并发(前提是你的 CPU 不是某 CPU)...

    5. 取模优化(仅O2)

      1 //设模数为 mod
      2 inline int inc(int x,int v,int mod){x+=v;return x>=mod?x-mod:x;}//代替取模+
      3 inline int dec(int x,int v,int mod){x-=v;return x<0?x+mod:x;}//代替取模-
      stdfour
    6. 前置 ++

      后置 ++ 需要保存临时变量以返回之前的值,在 STL 中非常慢。事实上,int 的后置 ++ 在实测中也比前置 ++ 慢 0.5 倍左右(UOJ 上自定义测试)

    7. 不要开bool,所有bool改成char,int是最快的(原因不明)。

    8. if()else语句比()?():()语句要慢,逗号运算符比分号运算符要快。

    9. 数据结构用指针代替数组(个人觉得无关紧要)

      数组在用方括号时做了一次加法才能取地址!
      所以在那些计算量超大的数据结构中,你每次都多做了一次加法!!!在 64 位系统下是   long long 相加,效率可想而知。


 

自己总结一点点

1.把%2改成&1

1 int a;
2 scanf("%d",&a);
3 printf("%d",a%2);
4 //上面已经用烂了
5 //=============================================
6 //试试这个
7 int a;
8 scanf("%d",&a);
9 printf("%d",a&1);
juruone

2.两个数交换

 1 int a,b,t;
 2 scanf("%d%d",&a,&b);
 3 t=a;
 4 a=b;
 5 b=t;
 6 //又是用烂了的法宝
 7 //=============================================
 8 //看看这个吧
 9 int a,b;
10 scanf("%d%d",&a,&b);
11 a^=b;
12 b^=a;
13 a^=b;
juruotwo

3.用define const等简化代码

1 #define fp(i,l,r) for(register int i=(l);i<=(r);++i)
2 
3 #define fd(i,l,r) for(register int i=(l);i>=(r);--i)
4 
5 const int MAXN=1000
6 
7 ···
juruothree

4.结合多个函数,下面只是举个例子

 1 inline int maxx(int a,int b){
 2     if(a>=b){
 3         return a;
 4     }
 5     else{
 6         return b;
 7     }
 8 }
 9 
10 inline int minn(int a,int b){
11     if(a>=b){
12         return b;
13     }
14     else{
15         return a;
16     }
17 }
18 //maxx和minn的函数
19 //=============================================
20 //下面是整合
21 inline int botposs(int a,int b,int pd){
22     if(pd==1) return a>=b?a:b;
23     if(pd==0) return a>=b?b:a;
24 }
juruofour

5.神器:读入优化

 1 #include <cstdio>
 2 #include <iostream>
 3 using namespace std;
 4 void input(int &x){
 5     char c=getchar();
 6     x=0;
 7     while(c<'0'||c>'9'){
 8         c=getchar();
 9     }
10     while(c<='9'&&c>='0'){
11         x=x*10+c-48;
12         c=getchar();
13     }
14 }
15 void output(int x){
16     int num=0;
17     char c[105];
18     while(x){
19         c[++num]=(x%10)+48;
20         x/=10;
21     } 
22     while(num){
23         putchar(c[num--]);
24     } 
25 }
26 int main(){
27     int a,b;
28     input(a);
29     input(b);
30     output(a+b);
31     return 0;
32 }
juruofive

 

3.4.两点仅仅是使代码看起来比较简洁


 

 

 

posted @ 2018-02-02 15:57  Fraction  阅读(265)  评论(0编辑  收藏  举报




















































Contact with me