其他-一些自己总结的卡常技巧

Posted penth

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了其他-一些自己总结的卡常技巧相关的知识,希望对你有一定的参考价值。

偶然发现自己代码的常数还算小?

于是乎总结了一下自己发现的一些常数技巧(还没写完,后续会更)

各位看官请耐心看完……前面都是大家知道的,后面会写些自己发现的东西

register

  • 将变量放在寄存器内,每次访问速度更快
  • 定义在变量前(如:register int i=1;
  • 寄存器大小有限,放不下太多变量(放太多会导致反而变慢)
  • 不能定义为全局变量
  • 单纯循环(1e9)时,不加register跑需(2s),加了仅需(0.2s)
  • 建议在循环次数较多的for的变量前加register
  • 在一些比赛中会被自动过滤

inline

  • 将函数进行类似define的东西,使得访问函数速度大大增加
  • 定义在函数名前(如inline int find(int x){return x;}
  • 若在有递归操作或有大量循环操作的函数前加inline是没有用的
  • 建议在一些很短的函数前加inline
  • 在一些比赛中会被自动过滤

循环展开

  • 将循环内的东西分块合并,例如:
  • for(int i=1;i<=n;++i)a[i]=1;
  • 可以变为
  • for(int i=1;i<=n;i+=4)a[i]=a[i+1]=a[i+2]=a[i+3]=1;
  • 展开越多貌似越快,注意不要数组越界

前置++

  • 一般都用++i,而不是i++

少用if-else

  • 据学长所言,速度是(if>(?:)>if\_else)
  • 注意使用三目运算符的时候注意优先级问题,之前就有过惨痛经历

比如:

x?get1(),work1():get2(),work2()

看上去很像是:

if(x) {get1(); work1();}
else {get2(); work2();}

但实际上它是这样的:

if(x){get1(); work1();}
else get2();
work2();

为什么?因为它读取到冒号后的第一个逗号就截止了,以为逗号后面是下一条语句

多用define

  • define的用处是替换代码,编译程序的时候直接将代码替换后再编译
  • 可以定义一些短函数,使得程序不用调用函数,比如#define f(x) (x*x)
  • 可以定义一些常量,比如#define p (1e9+7)
  • 可以用于精简代码,比如#define For(i,j,k) for(int i=j;i<=k;++i)
  • 可以用于纪念,比如说#define Formylove return 0,最后就只要Formylove

注意事项

  • 谨慎使用define定义函数,比如定义#define min(x,y) (x<y?x:y)时,若x为一个递归函数,会使得程序调用两次这个函数,若在线段树上使用这个,会使得复杂度变为(log^2n)
  • 注意优先级问题,比如定义#define f(x,y) x+y,如果外部调用为z*f(x,y),就会变为z*x+y

少用乘除

  • 据测试,运算符速度排名为位运算>加减号>乘法>除法>取膜
  • 所以尽量使用位运算(但(x*10)不要拆成((x<<3)+(x<<1))
  • 多用加减号代替乘除(这个不多说)
  • 多用乘法代替除法(比如实数的/2可以替换为*0.5

快速读入

  • 贴个板子就走人

考场实用型:

template <typename _Tp> inline _Tp read(_Tp&x){
    char c11=getchar(),ob=0;x=0;
    while(c11^‘-‘&&!isdigit(c11))c11=getchar();if(c11==‘-‘)ob=1,c11=getchar();
    while(isdigit(c11))x=x*10+c11-‘0‘,c11=getchar();if(ob)x=-x;return x;
}

int main(){read(x);}

刷速冲榜型:

struct ios {
    inline char read(){
        static const int IN_LEN=1<<18|1;static char buf[IN_LEN],*s,*t;
        return (s==t)&&(t=(s=buf)+fread(buf,1,IN_LEN,stdin)),s==t?-1:*s++;
    }
    template <typename _Tp> inline ios & operator >> (_Tp&x){
        static char c11,boo;
        for(c11=read(),boo=0;!isdigit(c11);c11=read()){if(c11==-1)return *this;boo|=c11==‘-‘;}
        for(x=0;isdigit(c11);c11=read())x=x*10+(c11^‘0‘);boo&&(x=-x);return *this;
    }
} io;
int main(){io>>x;}

以下都是博主自己的经验,不保证完全正确,dalao勿喷

头文件

  • 根据博主长久以来的测试,发现头文件定义的速度排名为:
  • 第一:单个常规头文件
  • 第二:一个万能头文件
  • 第三:一堆常规头文件
  • 所以给出建议,若只需一个头文件,就只打对应的那一个头文件,若需要不止一个头文件,打万能头文件bits/stdc++.h

定义变量

  • 定义一个变量是要时间的,可以将变量尽量地提到外面(如在for循环内定义变量不如在for循环外部定义,可以减少定义次数
  • 若能开全局变量最好

合并语句

  • 将互不相关的语句进行合并,语句之间使用逗号相连
  • he=1;ta=1;q[1]=s;可以合并为he=ta=1,q[1]=s;可以进一步合并为q[he=ta=1]=s;

位运算

  • 联赛选手都熟知的东西
  • x*2替换为x<<1x/2替换为x>>1x%2替换为x&1

下面是一些其他的技巧

矩乘

  • (APIO2018)的讲课上提到过矩阵乘法的变量枚举顺序最快为ikj(当然想更快可以矩阵分块)
  • 如果想更快,可以使用前面提到的访问优化
  • 即将(A[k][j])保存下来:
for(int i=0;i<A.n;++i)
    for(int k=0;k<A.m;++k){
        ll t = A[i][k];if(!t)continue;
        for(int j=0;j<B.m;++j)
            res[i][j]=(res[i][j]+t*B[k][j])%p;
    }

gprof

  • 这个是一个检测自己程序运行效率的东西

举个例子,若要查看程序a.cpp的效率,可以输入以下命令:

  • g++ -o a a.cpp -pg
  • ./a
  • gprof -b ./a > res

然后在对应文件夹里就会出现一个叫res的文件,里面即是程序这次在各个函数内运行的时间,然后就可以找到那个拖累程序的函数进行优化了

以上是关于其他-一些自己总结的卡常技巧的主要内容,如果未能解决你的问题,请参考以下文章

记录--九个超级好用的 Javascript 技巧

提效小技巧——记录那些不常用的代码片段

面试成长总结帖

高效的jQuery代码编写技巧总结

自己工作技巧的总结

移动端开发的一些技巧总结