OI比赛常数优化

这是一篇玄学文章

 

一、编译优化

1 #pragma GCC optimize("O3")
2 #pragma G++ optimize("O3")
预处理开O3优化

比赛时除非遇到常数很大可能会卡的暴力题否则一定不要用!玩火自焚!

 

二、I/O优化

核心:利用getchar()和putchar()这两个底层函数和位运算加速

输入优化(超逼格写法)

 1 #include<cstdio>
 2 #include<cctype>
 3 using namespace std;
 4 int read()
 5 {
 6     int x=0,f=0;
 7     char c=getchar();
 8     while(!isdigit(c))
 9     {
10         f|=c=='-';
11         c=getchar();
12     }
13     while(isdigit(c))
14     {
15         x=(x<<3)+(x<<1)+(c^48);
16         c=getchar();
17     }
18     return f?-x:x;
19 }
输入优化

这里为什么可以用c^48代替c-48('0')呢?因为48的二进制是110000,后4位均为0,48~57('9')近在后四位后变化,所以^48可以用来代替-48且更快

听说用fread更快但只能写文件,这里就不写代码了,通常比赛时使用上面的优化即可,有兴趣的读者可以查阅资料了解一下

输出优化

每次使用putchar()输出字符可加速输出

实测递归+putchar()输出整数的速度比printf()快6倍!

但不要写递归版……递归版的速度还慢于printf

放上代码

#include<cstdio>
#include<cctype>
using namespace std;
#define re register int
int stk[111],tt;
void print(int x)
{
    if(x==0)
        putchar('0');
    else
    {
        if(x<0)
            putchar('-'),x=-x;
        tt=0;
        while(x)
        {
            stk[++tt]=x%10;
            x/=10;
        }
        for(re i=tt;i;i--)
            putchar(stk[i]|48);
    }
}
输出优化

格式控制直接手打putchar()即可

stack[i]|48的正确性由输入优化的解释和或运算的定义显然可知

 

三、寄存器变量

需要大量运算的变量申请为寄存器变量,即register+类型名

通常将循环变量设为寄存器变量

另外将register int设为字符串常量可以简化代码,详见下面代码

1 #define re register int
2 int main()
3 {
4     for(re i=1;i<=999999;i++)
5     return 0;
6 }
申请寄存器变量

不要滥用register,一方面少量变量修改优化效果不明显,另一方面寄存器放不下变量时就会自动把变量放到内存里……

 

四、取模优化

适用于只有加减运算的题目

int inc(int x,int v,int mod){x+=v;return x>=mod?x-mod:x;}//代替取模+
int dec(int x,int v,int mod){x-=v;return x<0?x+mod:x;}//代替取模-
取模优化

 

五、memset,memcpy以及memmove

memset(数组名+第一个操作数下标,0/-1,操作大小*类型所占字节数)

memcpy(目标数组名+该数组第一个操作数下标,被copy数组名+该数组第一个操作数下标,操作大小*类型所占字节数)

memmove(目标数组名+该数组第一个操作数下标,被清空数组名+该数组第一个操作数下标,操作大小*类型所占字节数)

实例:

1 //int是4个字节所以用<<2
2 memset(a+l,0,r-l+1<<2);
3 memcpy(a+l,b+l,r-l+1<<2);
4 memmove(a+l,b+l,r-l+1<<2);
memset,memcpy和remmove实例

获取类型所占字节数方法:sizeof(类型名)

 

六、正常的优化

思路:减少运算及比较次数&用位运算代替四则运算

 

七、程序框架

将register int和long long缩写可以节省时间……用define就行

另外多设一个INF

#include<cstdio>
#include<cctype>
using namespace std;
#define re register int
#define ll long long
const int INF=0x3f3f3f3f;
int stk[111],tt;
void print(int x)
{
    if(x==0)
        putchar('0');
    else
    {
        if(x<0)
            putchar('-'),x=-x;
        tt=0;
        while(x)
        {
            stk[++tt]=x%10;
            x/=10;
        }
        for(re i=tt;i;i--)
            putchar(stk[i]|48);
    }
}
int read()
{
    int x=0,f=0;
    char c=getchar();
    while(!isdigit(c))
    {
        f|=c=='-';
        c=getchar();
    }
    while(isdigit(c))
    {
        x=(x<<3)+(x<<1)+(c^48);
        c=getchar();
    }
    return f?-x:x;
}
int main()
{
    return 0;
}
程序框架

 

结束语

此篇文章介绍的优化均已实测证明,下面是一些误传:

  1. 迷信inline?实测inline还要稍微慢上一丢丢……
  2. 前置++和后置++实测在单独语句时没有任何差别
  3. 用三目运算符(?:)代替if()else()?然而实测三目运算符比if()else()还要慢一些……
posted @ 2018-08-22 22:55  李昊哲  阅读(672)  评论(0编辑  收藏  举报