• 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倍,不是所有的库函数都是cc++实现的。 
    (我记错了吗..还是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.两点仅仅是使代码看起来比较简洁


     

  • 相关阅读:
    YII2安装中遇到的错误解决Calling unknown method: yiiwebUrlManager::addRules()
    BZOJ 2049: [Sdoi2008]Cave 洞穴勘測 LCT
    【C语言】编写一个函数实现n^k,使用递归实现
    Kivy A to Z -- 怎样从python代码中直接訪问Android的Service
    java并发 使用ScheduledExecutor的温室控制器--thinking in java 21.7.5
    算法之-归并排序算法,插入排序算法
    J2EE之ServletContext读取资源文件
    STOMP 客户端 API 整理
    jQuery easyui datagrid 的数据加载
    扩展struts2的结果集StrutsResultSupport 自定义Result处理JSON
  • 原文地址:https://www.cnblogs.com/Fraction/p/8405601.html
Copyright © 2020-2023  润新知