卡常小技巧
常系数尽量用 2 的幂次
这样可以用很多位运算,当然可以优化。不过应当使得常数尽量平衡。(比如基数排序的时候一般用接近 的 时最快的,bitset
比较小的时候也有点用,不过会多占空间。)
内存分配
容器相关
STL 都大量使用了动态的内存分配,导致它们都很慢,一种改进方式是先 reserve 一下。
正确的 vector 建图
for(int i=1,x,y;i<=m;++i)
e[x].emplace_back(y),
e[y].emplace_back(x);
这样用 e
时 cache 命中率低,可以改为
for(int i=1,x,y;i<=m;++i)
g[x].emplace_back(y),
g[y].emplace_back(x);
for(int i=1;i<=n;++i) e[i]=vector<int>(g[i].begin(),g[i].end());
这样就几乎和链式前向星一样快了。
重标号
有时候遍历一幅图或者一棵树的时候,我们可能走出如下路径:。
此时我们访问的内存是极其不连续的,导致 cache 命中率奇低。
在树上可以使用树链剖分重新标号,再重新建图的方式降低 cache miss,不过在本来遍历次数就很小时这样的预处理可能反而增大常数。
正确的快读长度
fread 一次读入的数据量应该和题目最大读入的字符量差不多大(这个要结合输入形式判断,不要忽略空格和换行),此时快读的优化效果最为明显。
正确的运算
乘法
一般我们认为这个是 的。(虽然实际上不是,但是乘法真的很快。)
__int128
会比自己手写的高精快很多,自信的话可以试试。
除法
不管是整除还是浮点数除法,它们都不是 的,而是和字长 有关的。
如果是整除常数,可以用 Barrett Reduction 优化;如果值域不大(比如分块的时候),可以把所有结果不用除法的预处理出来。
如果是浮点数除法,反复除以一个数,可以先算出其倒数,之后用倒数乘。(虽然这个看着很像编译器会做的优化,但是在函数很复杂的时候并不一定会这样。)
取模
龟速乘慢疯了,比 long double
和 __int128
都慢,如果是膜常数那可以用各种优化。(const
的变量编译器会自动优化。)
还可以积累数据直到下一次操作会爆值域才取模,不过写起来会比较繁琐,即使只积累一次也可以有显著的优化。(1ll*a*b%mod*c%mod
(__int128)a*(__int128)b*(__int128)c%mod
)
(unsigned
类型的变量取模快于 signed
)
位运算
有时候直接优化了复杂度,比如 bitset
可以搞出 之类的复杂度。
有多种写法的时候可以都试一试,说不定。
函数
内联
如果你函数太复杂,即使没有递归,inline
也可能失效,此时可以使用 __attribute__(__always_inline)
代替 inline
,不过真的太复杂了也会不行。
递归
大家都直到递归很慢吧,所以能写递推就不要写递归,除非实在想不清楚。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构