Loading

卡常论

前言

时间复杂度仅供参考,请以实际效率为准。

即使复杂度正确的代码,也应该在极限数据下测试其效率,由data.exe与去掉暴力的check.exe共同完成。

同样,对于两个复杂度相同的算法,由于常数原因,一个跑得过,一个就是会TLE,卡常卡不过,就要换算法,不要总想着CCF的机子跑得快。黄奕涵就在省选被卡常了。


  1. 将递归改为递推

  2. 优化输入输出

  3. 确认复杂度真的没问题,避免忽略log2,__gcd等内置函数以及快速幂的复杂度

  4. 范围不要开到理论最大范围,实际是多少就用多少

  5. 手写STL

  6. 利用c++内置性质:inline,循环展开

  7. 随机打乱顺序

一个例子:TLE#13卡常方法


unordered_map的“进队优化”(林子睿在SCOI2024考场上用这个技巧给T1卡常使他进省队),亲测这样查询会非常快(但还是耐不住unordered_map常数实在太大了)。

auto it=mp.find(col);
if(it==mp.end())continue;
sum+=it->second;

在主要的复杂度部分秉承能减少一次就减少一次的观点:

代码来源:序列

原代码:

DP_G(),DP_F();
cout<<(sum+MOD)%MOD<<" ";
for(int j=1;j<=q;){
    DP_G(),DP_F();
    ...

改后代码:

for(int j=1;j<=q;){
    DP_G(),DP_F();
    if(j==1)cout<<(sum+MOD)%MOD<<" ";
    ...

\(80\to 100\)


一定不要用 stack 常数太大


throw直接跳出递归,例:R201788736


少用double,少取模


memset的限制大小初始化,例:R201999060

memset(vst,0,(U+1)*sizeof vst[0]);

换掉vector,常数就除2。再不行,也可以换成邻接表


减少循环个数可以卡常,这是因为减少了jump的次数,而jump是不连续访问,十分耗时


合并循环可以大大加速,

lop(j,0,i-(i>0))
    lop(x,0,i)
        (g[i][j]+=f[i&1][x][j]*a[x+1])%=MOD;
lop(j,0,i-(i>0))
    lop(x,0,i)
        (h[i][j]+=f[i&1][x][i-(i>0)-j]*b[x+1])%=MOD;

变为:

lop(j,0,i-(i>0)){
    lop(x,0,i){
        (g[i][j]+=f[i&1][x][j]*a[x+1])%=MOD;
        (h[i][j]+=f[i&1][x][i-(i>0)-j]*b[x+1])%=MOD;
    }

能存就存,减少取模

(f[(i+1)&1][j+1][k+(i>0)]+=f[i&1][j][k])%=MOD;
(f[(i+1)&1][j][k]+=(k+(i>0))*f[i&1][j][k])%=MOD;
(f[(i+1)&1][j][k+1]+=(i-(i>0)-k)*f[i&1][j][k])%=MOD;

变成

int&val=f[i&1][j][k];
if(!val)continue;
val%=MOD;
f[(i+1)&1][j+1][k+(i>0)]+=val;
f[(i+1)&1][j][k]+=(k+(i>0))*val;
f[(i+1)&1][j][k+1]+=(i-(i>0)-k)*val;

提前存下,减少寻址

lop(i,0,n-1)
    lop(j,0,i)
    	lop(k,0,n-i)
   			(w[i][k]+=c[j+k+(i>0)]*g[i][j])%=MOD;

变为

lop(i,0,n-1)
    lop(j,0,i){
        int g_val=g[i][j];////有用!
        lop(k,0,n-i)
            (w[i][k]+=c[j+k+(i>0)]*g_val)%=MOD;
    }
posted @ 2024-08-23 09:59  lupengheyyds  阅读(46)  评论(0)    收藏  举报