一些科技

不要被科技反噬!

只会记录平时使用频率较高/能在赛时写出来的科技。过于阴间的不写。

数据结构相关

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\) 算法:

\[\gcd(a,b)= \begin{cases} 2\gcd(a/2,b/2)&(a,b\ \text{为偶数})\\ \gcd(a/2,b)&(a\ \text{为偶数},b\ \text{为奇数})\\ \gcd(|a-b|,\min(a,b))&(a,b\ \text{为奇数}) \end{cases}\]

看似这个算法实现中的递归层数会大于普通辗转相除法,但是此算法可以大量和位运算结合极大简化运算过程,实现惊人的效率。
__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")

参考资料

RMQ - Kewth

论现代硬件上的常数优化 - 宋佳兴

武装直升机 - Undead2008

posted @ 2025-11-06 10:00  headless_piston  阅读(11)  评论(0)    收藏  举报