算法复习

省选前最后一周了,对照大纲过一遍,每个算法稍微写一点自己的理解与板子记忆技巧。

感觉很多东西还是之前听别人讲的时候学的似懂非懂......导致现在看起来好像啥都会一点实际上好像啥也不会,每次越临近考试就会感觉整个人很虚无啊......反正不是很好受。

主要写一些我不太会的,所以没啥参考意义。

数论

把不熟的板子题写一遍,exLucas 学一下。

  • exgcd【7】

用于解决形如 \(ax+by=c\) 的整数域不定方程,有解的条件是 \(\gcd(a,b)|c\)(裴蜀定理 【7】)。

具体地,对于方程 \(ax+by=c\),辗转相除构造 \(a=b',b=a \bmod b\) 的解 \((x',y')\),那么 \(x=y'\)\(y=x'-\lfloor\frac{a}{b}\rfloor y'\),边界条件 \(b=0\)\(x=1,y=0\),容易验证其正确性。

得到是一组特解,如果需要求最小最大解 / 正整数解之类的其他要求,直接调整即可。所有解和特解的关系形如 \(x=x_0+kb/g,y=y_0-ka/g\),其中 \(g=\gcd(a,b)\)

void exgcd(ll a,ll b,ll&x,ll&y){
	if(!b)return x=1,y=0,void();
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}
  • 费马小定理 【7】

\(a^{p-1}\equiv 1\pmod p\)\(p\) 是质数,常用于求模质数意义下的逆元。

  • 威尔逊定理 【7】

\((p-1)!\equiv -1\pmod p\),证明考虑两两分组乘起来。

  • 欧拉定理和欧拉函数 【7】

欧拉函数 \(\varphi(n)\) 表示 \([1,n]\) 中与 \(n\) 互质的正整数个数,是积性函数,可以线性筛求。

常用结论:\(\sum\limits_{d|n}\varphi(d)=n\),证明考虑组合意义。

(扩展)欧拉定理:当 \(b\ge \varphi(p)\)\(a^b\equiv a^{b\bmod \varphi(p)+\varphi(p)}\pmod p\)(欧拉降幂法)

  • exCRT 【7?】

exCRT 可以完全替代 CRT。

要求形如 \(x\equiv a_i \pmod {p_i}\) 的方程组。

考虑依次合并各个方程组,那么现在问题变为,已知前 \(i-1\) 个方程组的某个解为 \(x\),前 \(i-1\)\(p\) 的 LCM 为 \(M\),那么调整这个 \(x\),加上 \(M\) 的倍数使得 \(x\) 满足第 \(i\) 个方程组。用形式化的语言描述就是 \(x+Mt\equiv a_i\pmod{p_i}\),移项得到 \(Mt\equiv a_i-x\pmod{p_i}\),可以表示为 \(Mt+p_is=a_i-x\) 的二元一次不定方程的形式,用 exgcd 解决。

  • 原根和指数 【8】

阶:对于 \(a,m\)\(a\)\(\bmod m\) 意义下的阶定义为最小的 \(p\) 满足 \(a^p\equiv 1\pmod m\)

原根:阶为 \(\varphi(m)\)\(a\)

如果求得了一个原根 \(a\),那么可以得到所有原根 \(a^k\),其中 \(\gcd(k,\varphi(m))=1\),所以如果一个数有原根,个数一定是 \(\varphi(\varphi(m))\)

\(2,4\) 和形如 \(p^{k},2p^{k}\)(其中 \(p\) 是奇质数)的数有原根。

检验一个数 \(x\) 是否是原根,需要满足 \(x^{\varphi(m)}=1\),且 \(x^{\varphi(m)/p}\neq 1\),其中 \(p\)\(\varphi(m)\) 的质因子。

最小原根是 \(\mathcal O(n^{1/4})\) 级别的。

常用技巧:将乘法问题通过求原根的离散对数转化为加法问题。

  • exBSGS 【8】

主要思想:meet in the middle。

目标:求解 \(a^x\equiv b\pmod p\)

\(t=\sqrt(2\varphi(p))\),那么预处理出 \(a^{kt}\)\(a^{k}\)\(k\in [0,t]\))。

\(x=ft-g\),那么枚举 \(g\le t\),一个必要条件是 \(a^{ft}\equiv ba^g \pmod p\),找到对应的 \(f,g\) 中的前两个,带回检验(条件不是充分的)。只需要代入前两个是因为循环成 \(\rho\) 形,只有前两次有效。

存储使用 unordered_map 或者 gp_hash_table

顺便 mark 一下这个 pbds 咋用:需要引入头文件 #include<ext/pb_ds/assoc_container.hpp>,并 using namespace __gnu_pbds,剩下和 unordered_map 一样用就行了。

  • exLucas 【?】

\(\binom{n}{m}\bmod p\)\(n,m\le 10^{18},p\le 10^6\)

\(p\) 分解为若干个形如 \(p^k\) 的子问题,最后用 exCRT 合并,下面考虑一个 \(p^k\) 的求解方式。

不能方便地计算 \(\binom{n}{m}\) 的问题主要在于包含质因子 \(p\) 的数不能求逆元,所以不妨先把所有的 \(p\) 分解出来。记 \(c(x)\)\(x\) 包含的 \(p\) 因子个数,可以 \(\mathcal O(\log n)\) 求出一个 \(c(x)\)。现在要做的就是计算形如 \(\frac{n!}{p^{c(n!)}}\) 的事情,最后答案乘上多出来的 \(p\) 因子。设 \(f(n)\) 为如上这件事情,那么可以分治到规模为 \(n/p\) 的子问题,要乘上 \(n/p^k\)\([1,p^k]\) 中非 \(p\) 倍数的数的乘积,这件事可以预处理。现在就可以 exgcd 求逆元了,复杂度 \(\mathcal O(p\log p)\)

  • 莫比乌斯反演 【9】

  • 二次剩余 【10】

可能是你大纲最简单的 10 级,可以会一下。

目标:求解 \(x^2\equiv n\pmod p\)

定义勒让德符号 \((n/p)\) 为:\(n\)\(\bmod p\) 意义下为二次剩余时 \(=1\)\(n\equiv 0\pmod p\) 时等于 \(0\),否则等于 \(-1\)

判断勒让德符号:当 \(p\) 为奇质数时,\(n^{(p-1)/2}\equiv 1\pmod p\) 说明 \(n\) 是二次剩余。

求解二次剩余运用 Cipolla 算法。首先,随机出一个 \(t\) 满足 \(t^2-n\) 不是二次剩余,设 \(w^2\equiv t^2-n\pmod p\)(虚数单位),那么可以构造 \((n+w)^{(p+1)/2}\) 为合法的二次剩余。

二次剩余相关的一些性质(\(p\) 为奇质数):两两配对和为 \(p\)\([1,p-1]\) 中恰好一半是二次剩余。

数据结构

把板子过一遍,重点是平衡树。

  • 平衡树 【8】

熟练写的只有 fhq treap,虽然可能因为一个月没写过平衡树题了又变得不熟练了,不讲了。这几天打两遍板子。

  • 可合并堆 【8】

pbds 启动。

  • 虚树 【10】

虚假的十级算法。

一个简单的虚树建法:把关键点拿出来按照 dfs 序排序,将相邻的 lca 加入后再次排序并去重,此时 \(LCA(a_{i-1},a_i)\) 就是 \(a_i\) 在虚树上的父亲,比那个栈方法不知道高到哪里去了。

记得写一道没写过的板子题,比如消耗战。

图论

  • tarjan:强连通,边双,点双,割点,割边,圆方树 【7】

强连通:记录 dfs 栈,遍历结束后 low[x]==dfn[x] 判断。

边双:找割边,对于 \((x,y)\),如果 low[y]>dfn[x] 说明回不去,是割边。边双只要把割边断开搜一遍就行了。

割点:对于非根节点,只要存在一个儿子能回到的最高点在 \(x\) 子树内 low[y]>=dfn[x] 就说明 \(x\) 是割点,对于根节点需要判断儿子个数 \(\ge 2\)

点双:和找割点类似,每次出现 low[y]>=dfn[x] 就不断弹出搜索栈中的节点,直到出现 \(y\),并把 \(x\) 也加入这个分量里,注意特判孤立点。

圆方树:每个点双往一个方点连边,连成菊花就行了。

还有一个玩意儿叫做 Kosaraju,可以用 bitset 优化。我会背板子,牛牛牛。

没想到 NOI2023 后的半年居然能一遍全部写对。

  • 次小生成树 【7】

  • 次短路 【7】

  • 欧拉路 【6】

H 开头忘了叫啥算法了。反正一路 dfs,记一下当前弧,最后倒着输出就行了。注意判无解。

如果用前向星写的话,注意字典序的排序应该是反的。

  • 2-SAT 【8】

一般会拆点 \((x,0)\) \((x,1)\) 表示选 \(0/1\),那么 \(x=i\) 必须推出 \(y=j\) 就可以写成 \((x,i)\)\((y,j)\) 连边的形式,如果 \((x,0)(x,1)\) 在同一个强连通分量里就无解。

构造方案的时候如果 \((x,0)\) 的强连通分量编号小于 \((x,1)\) 那么取 \(0\),否则取 \(1\)

  • 网络流 【8】

板子我会打,真牛。

上下界,不太会。

  • 匈牙利算法 【8】

板子我会打,真牛。

好像打的不太好,抽空重打。

  • KM 算法 【9】

板子我不会打,真不牛。

数学

  • 高斯消元法 【7】

板子不太会打,难受了。

现在会了。舒服了。

  • 群论,burnside,polya 【9】

这个可以先开摆。

  • Stirling 数 【9】

  • Prufer 序列 【9】

  • 行列式,LGV 【9】

板子不太会打,难受了。

  • 向量空间与线性相关 【9】

  • 快速傅里叶变换 【10】

  • SG 函数 【9】

sg 函数的定义是其所有后继状态的 sg 函数的 mex,其中无出边的状态 sg 值为 \(0\)

多重游戏的结论是:所有状态 sg 异或和 \(\neq 0\) 先手胜。

  • 计算几何 【摆了】

字符串

  • KMP 【5】

板子我会打,牛牛。

  • Manacher 【8】

考虑实时维护当前所有回文串中右端点最靠右的,设其回文中心为 \(id\),右端点为 \(r\)

那么计算 \(i\) 的回文半径的时候可以直接从 \(\min(r-i+1,R_{2\times id-i})\) 开始枚举(因为 \([id-r+1,id+r-1]\) 是回文的,所以 \(i\) 的问题可以到 \(i\) 的对称点记录,并且不能超过最大的长度),然后枚举就是均摊线性的了。

注意添加分隔符以计算长度为偶数的回文串并避免越界。

  • SA 【8】

  • ACAM 【8】

板子在 bfs 建 fail 树的时候两处都是 ch[fail[u]][i],总是写错,有点难受。

问题可以试着放到 fail 树上看。

求出现次数可以认为是求作为每个前缀的后缀次数。比如说求 \(t\)\(s\) 中的出现次数,可以先找到 \(s\) 的前缀状态 \(s_1,\dots s_n\),那么出现次数就是 \(t\) 的子树内 \(s\) 状态的个数。

  • exKMP 【9】

  • SAM 【10】

杂项

  • A,IDA 【7】

  • dp 的各种优化方法

  • pbds 的使用方法

  • lg / loj 上的一些其他模板。

posted @ 2024-02-21 22:17  PetitSouris  阅读(264)  评论(0)    收藏  举报