一些科技
不要被科技反噬!
只会记录平时使用频率较高/能在赛时写出来的科技。过于阴间的不写。
数据结构相关
RMQ
这里不讨论离线。
朴素做法有线段树的 \(O(n)-O(\log n)\) 和 ST 表的 \(O(n\log n)-O(1)\)。
线性的做法太阴间,学不来。
询问区间随机
由乃救爷爷
分块,设块长为 \(B\),预处理每个块的前后缀最大值,时间复杂度 \(O(n)\)。
- 若询问区间跨块,显然我们只需考虑整块,而整块数量已经被我们降至 \(O\left(\cfrac{n}{B}\right)\),所以直接 \(O\left(\cfrac{n}{B}\log \cfrac{n}{B}\right)\)-\(O(1)\) 做就可以。
- 若询问区间在同一块内,直接 \(O(B)\) 暴力。询问的 \(l,r\) 在同一块内的概率是 \(\cfrac{B}{n}\),所以期望单次复杂度 \(O\left(\cfrac{B^2}{n}\right)\)。
令 \(B\) 取 \(O(\log n)\) 可以实现 \(O(n)\) 的预处理,取 \(O(\sqrt n)\) 可以实现期望 \(O(q)\) 的询问复杂度。如果 \(B\) 取 \(O(\sqrt n)\) 也可以直接 \(O(\sqrt n^2)\) 预处理所有整块跨块,不必使用 ST 表了(此事在 [Violet] 蒲公英中亦有记载)。
非递归线段树
伟大无需多言。
https://www.cnblogs.com/headless-piston/p/19085148
分块
离线逐块处理可以在一些复杂的分块题中保持线性空间,而且在减小常数方面也有一定作用。如 [Ynoi2018] 五彩斑斓的世界。
bitset
#include<vector>
using namespace std;
typedef unsigned long long ull;
struct Bitset{
size_t siz=0;
vector<ull> x;
inline void init(size_t SIZ){siz=SIZ;x.resize((SIZ>>6)+((SIZ&63)?1:0));}
inline void set(const size_t &a){x[a>>6]|=1ull<<(a&63);}
inline void set(){
x.assign(x.size(),~0ull);
if(siz&63) x.back()&=(1ull<<(siz&63))-1;
}
inline void reset(const size_t &a){x[a>>6]&=~(1ull<<(a&63));}
inline void reset(){x.assign(x.size(),0ull);}
inline void flip(const size_t &a){x[a>>6]^=1ull<<(a&63);}
inline bool ask(const size_t &a)const{return x[a>>6]>>(a&63)&1;}
inline Bitset &operator^=(const Bitset &a){
for(size_t i=0;i<x.size();i++) x[i]^=a.x[i];
return *this;
}
inline Bitset split(const size_t &l,const size_t &r)const{
Bitset res;
size_t len=r-l+1;
res.init(len);
size_t block=l>>6,bit=l&63;
for(size_t i=0;i<res.x.size();i++){
res.x[i]=(x[i+block]>>bit);
if(bit&&i+block+1<x.size()) res.x[i]|=x[i+block+1]<<(64-bit);
}
if(len&63) res.x.back()&=(1ull<<(len&63))-1;
return res;
}
inline Bitset operator^(const Bitset &x)const{return Bitset(*this)^=x;}
};
分块 bitset 是常用的卡空间方式,如果需要维护 \(O(n)\) 个 bitset 但是每个 bitset 之间只相差 \(1 \operatorname{bit}\),可以考虑只记录 \(\sqrt n\) 个 bitset,每次 \(O(\sqrt n)\) 计算需要的那个 bitset,而且这样以后可以 \(O(\sqrt n)\) 修改。(此事在 [省选联考 2025] 追忆中亦有记载)
另一种卡空间方式是离线后分组处理。(此事在 【模板】三维偏序(陌上花开)中亦有记载)
平衡树
01 Trie 和块状链表也是不错的平衡树。
数学相关
Binary GCD
一种基于二进制分解的高速 \(\gcd\) 算法:
看似这个算法实现中的递归层数会大于普通辗转相除法,但是此算法可以大量和位运算结合极大简化运算过程,实现惊人的效率。
__builtin_ctz() 返回末尾 \(0\) 的数量,即 “count trailing zeros”。利用这个函数配合右移操作,我们可以一次性完成多次除以 \(2\) 操作。
具体地:
- 首先将 \(a,b\) 中的 \(2\) 除尽;
- 当 \(a,b\) 都是奇数时,会递归到 \(\gcd(|a-b|,\min(a,b))\),此时可以发现 \(|a-b|\) 为偶数,再次把 \(2\) 除尽后又回到都是奇数的情况,直到 \(a,b\) 相等。
- \(a-b\) 和 \(b-a\) 的 \(\operatorname{ctz}\) 是相等的,可以与 \(\operatorname{abs}\) 的计算并行加速。
int GCD(int a,int b){
if(!a|!b) return a|b;
int az=__builtin_ctz(a);
int bz=__builtin_ctz(b);
int z=min(az,bz);
a>>=az,b>>=bz;
while(a!=b){
int diff=b-a;
az=__builtin_ctz(diff);
b=min(a,b),a=abs(diff)>>az;
}
return a<<z;
}
可以直接草过基于值域预处理的快速 GCD。
图论相关
图的存储
链式前向星存图较快,因为内存访问连续,而且相对地,vector 动态扩容的常数较大。
vector 遍历速度较快,因为内存访问连续。
vector 的建图速度可能慢的超乎你的想象,在 \(10^6\) 量级以上的稀疏图中尤其明显。
有一种兼具 vector 和链式前向星优点的存图方式:首先统计每个点的度数,对度数做前缀和,这样在边数组中就可以实现连续地存储一个点的出边,并通过度数前缀和 \(O(1)\) 定位其在边数组中的位置。
constexpr int N=1e5+10,M=2e5+10;
int n,m,u[M],v[M],d[N],e[M];
void build1(){//有向
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i];
++d[u[i]];
}
for(int i=1;i<=n;i++) d[i+1]+=d[i];
for(int i=1;i<=m;i++) e[--d[u[i]]]=v[i];
}
void build2(){//无向
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i];
++d[u[i]],++d[v[i]];
}
for(int i=1;i<=n;i++) d[i+1]+=d[i];
for(int i=1;i<=m;i++){
e[--d[v[i]]]=u[i];
e[--d[u[i]]]=v[i];
}
}
bool vis[N];
void dfs(int u){
if(vis[u]) return;
vis[u]=1;
for(int i=d[u];i<d[u+1];i++) dfs(e[i]);
}
其他
分支预测优化
利用函数 __builtin_expect():
long __builtin_expect(long expression,long value);
其中 expression 是整型表达式,就是你放进 if 条件里的东西,value 是我们期望 expression 会等于的值。
比如:
if(__builtin_expect(ptr==nullptr,0)) return;//一般不会遇到空指针
else{
//do something...
}
爆改矿务局
火车头
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
砍伐冷冻库
让编译器自行进行指令集优化、浮点数优化,禁用栈溢出保护
#pragma GCC optimize("Ofast,no-stack-protector,fast-math",2,3)
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")

浙公网安备 33010602011771号