卡常论
前言
时间复杂度仅供参考,请以实际效率为准。
即使复杂度正确的代码,也应该在极限数据下测试其效率,由data.exe与去掉暴力的check.exe共同完成。
同样,对于两个复杂度相同的算法,由于常数原因,一个跑得过,一个就是会TLE,卡常卡不过,就要换算法,不要总想着CCF的机子跑得快。黄奕涵就在省选被卡常了。
-
将递归改为递推
-
优化输入输出
-
确认复杂度真的没问题,避免忽略
log2,__gcd等内置函数以及快速幂的复杂度 -
范围不要开到理论最大范围,实际是多少就用多少
-
手写STL
-
利用c++内置性质:
inline,循环展开 -
随机打乱顺序
一个例子: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;
}

浙公网安备 33010602011771号