洛谷noip金秋营
进阶算法
简单单调数据结构
-
单调栈
感觉都不是很困难()只要分析出来需要维护左右两边最值就可以了。。。
-
单调队列
使用要求:
-
实际可以做不定长的滑动窗口,但前提条件是区间左右端点都单调递增。
-
移动过程中队列内相对大小关系不变。
-
贪心
-
交换相邻元素
使用条件:
-
通过直接排序成立的贪心。
-
满足严格弱序(即 \(i < j, j < k \rightarrow i < k\))。
考虑:如果交换相邻两项使得答案更劣,则应当满足 xxx 式子。
- P1080 [NOIP2012 提高组] 国王游戏:有些关于前缀信息的式子,交换相邻两项只会影响该两项,于是我们可以约掉该前缀信息。
-
-
归纳法
-
大致的证明思路是:证明在以前决策的基础上,当前选择最优;以及当前决策对于以后,也是最优的(无后效性)。但实际上这道题并不是很典的归纳法。
-
首先第一步就十分巧妙:将区间每 \(k-1\) 个分为一组,每组内区间互不相交,组间随便。
于是现在需要证明:如果有不少于 \(k-1\) 种颜色,且每种颜色都恰好有 \(k\) 个,则一定可以取出 \(k-1\) 个互不相交、颜色不同的区间。
尝试正经归纳法。当 \(k=2\) 时,结论显然成立;当 \(k > 2\) 时,先尝试取出 \(r\) 最小的一个 \([l, r]\),而 \([1,r-1]\) 中一定不会有一种颜色出现过两次(否则 \(r\) 可以更小),故可以轻松归纳到 \(k-1\) 的情况。
之前做过的题目中,P10280 [USACO24OPEN] Cowreography G 这道大贪心的第一步(证明交换最小步数)也可以用到归纳贪心。做完以后复习一下国庆套题中 biall 那道题。
-
还有为什么不讲反悔贪心!我刚好对这个非常不熟悉!要练习!
倍增
-
跳步型倍增:树上、置换、内向基环树上倍增等(在有确定唯一后继时使用)
-
Lynyrd Skynyrd:
"won't you fly high free bird"建模为树,做树上倍增。 -
-
树上倍增可以做动态加叶子,线段树做不了(但是可持久化可以)。
-
两次树上倍增。(注:找树上第一个小于的祖先不可以用单调栈,因为均摊复杂度数据结构不支持撤销。)
-
-
-
拆分型倍增:二进制拆分/按位贪心,倍增预处理 \(2^k\) 的答案,然后组合出问题的答案,最简单的例子就是 ST 表。
- Vika and Wiki:主要是需要找到规律,即跳了 \(2^k\) 步它的值变为 \(a_i \operatorname{xor} a_{i + 2^k}\)。
分治
-
杂
-
CDQ 分治
核心思想在于将动态数点变为静态数点。
- AI robots:最巧妙的地方在于通过对 \(r_i\) 排序,将复杂的同时关于 \(i,j\) 的问题转化为了三维数点问题。
CDQ 分治优化 DP:先分治左半边、然后处理左半转移到右半、再分治右半。
数据结构
二叉堆
-
P2085 最小函数值:维护若干序列归并后的前 \(k\) 小值。
-
P2048 [NOI2010] 超级钢琴:与上一题不同的地方在于每个序列内部不一定具有单调性。可以用主席树维护。还有一个很巧妙的方法,每次在优先队列内维护元组 \((l, [p, q])\) 表示左端点 \(l\) 的最优左端点在 \([p, q]\) 中取得,然后用 ST 表维护 \([p, q]\) 中最值、并且每次将 \([p, q]\) 裂开即可。
-
P10977 Cut the Sequence:关键性质——\(f[i]\) 满足单调性。然后用单调队列 + 带 lazy-tag 的堆/multiset 维护。但实际如果没有发现单调性性质也可以用线段树优化 DP / 可合并堆来做。
并查集
-
P9565 [SDCPC2023] Not Another Path Query Problem:显然按位贪心 + 并查集辅助判断连通性。(注:此题存在一个变式,即求 \(s, t\) 之间按位与的最大值,但没法做到多测。)
-
P10350 [PA2024] Modernizacja Bajtocji
纯粹思维题。考察图论建模 + 并查集维护连通性。
-
将点与点之间连边。当一个连通块形成基环树时,该连通块内的所有点的情况才能确定。
-
删电脑操作这里读题容易读不清楚,不管我们之前是否能确定 \(c_i\) 是否有电脑,这里都可以执行该操作。于是,这个操作还可能帮助我们确定某些人是否有电脑。通过分析可以发现,只有此时连通块里只剩下一个人还没有被标记过没有电脑时,他可以确定。
-
一个人的电脑被删了以后,我们可以将它看作一个崭新的点加入图中,而将它原来的版本留在那里。
-
-
这道题的关键与其说是哈希,不如说是启发式合并。启发式合并有两种写法:
-
\(O(\log n)\) 查询,\(O(1)\) 合并。这种用的最多,而且复杂度是严格的。
-
\(O(1)\) 查询,均摊 \(O(n \log n)\) 合并。具体而言,这里我们是像其它启发式合并数据结构那样,暴力修改每一个较小集合。
然后再用哈希优化序列比较速度即可。
这题实现细节还不少。这里列举一点:
-
unordered_map 卡常方法
-
尽量减少在 map 中的无用元素,比如值变成 0 了就将它从 map 中删除。
-
尽量少写 mp[x],而是用 mp.find 找到对应位置后直接对迭代器操作。
-
-
单调队列优化 DP 卡常方法
-
手写
-
不每次计算值,而是储存一个 pair
-
-
关于哈希
从原理上可以有:随机数哈希 / 进制哈希;
然后是实现方式上:
-
双模数哈希:进制哈希和随机数哈希都可以使用。但是在本题这种需要 unordered_map 的情况下是没法做的。
-
自然溢出哈希:进制哈希一定不能写自然溢出,可以用 01 串卡掉。随机权哈希用它应该没有啥问题。
-
单模数哈希:模数绝对不能只取到 1e9 左右,会被卡。可以取的一个模数是 1e18+7,但需要注意初始化时须写作 (ll)1e18+7,并且计算过程中需要 __int128。
-
-
树状数组 & 线段树
-
P3801 红色的幻想乡:将横纵坐标拆为独立两部分维护。
-
P9561 [SDCPC2023] Colorful Segments:我设计 DP 转移时遇到了一些问题。难点在于:为什么枚举最后一个同色段,会比枚举上一个选中的区间更加简单?所以说 DP 转移时,当有多个可能的枚举对象时,一定要注意选择更简洁的。
扫描线,关键在于考虑:每一个询问区间左端点 \(l\) 要到哪一个位置时,当前元素 \(x\) 才可能有贡献。
字典树
字典树解决匹配问题(但实际一般用 AC 自动机,只是这里可以暴力):
- P2536 [AHOI2005] 病毒检测:没什么好说的,Trie 树上做搜索。。。
字典树更常见的应用是处理前缀关系:
-
P9694 [GDCPC2023] New but Nostalgic Problem:Trie 上的一类题目,类似线段树二分,求字典序最大、且权值和小于某一阈值的字符串。这里唯一需要特判的就是空串。
-
P7537 [COCI2016-2017#4] Rima:字典树转化为树上路径问题 + 将路径拆为向上/向下两段 + 树形 DP。
图论
树
-
LCA
求法:
-
倍增求 LCA。\(O(n \log n)\) 预处理 \(O(\log n)\) 查询。
-
由于 LCA 满足结合律,可以使用线段树维护区间 LCA。
-
\(O(n \log n)\) 预处理 \(O(1)\) 询问欧拉序 + RMQ。
- P4281 [AHOI2008] 紧急集合 / 聚会:分类讨论 LCA 情况证明。
-
-
树上差分 + 树上前缀和
-
P6845 [CEOI2019] Dynamic Diameter
精妙题。四个关键:
-
不用两次 DFS / 树形 DP 维护,而用树上差分转化为数点问题。
-
如果我们要对某个式子求最值,且该式子内部本身即包含最值,可以考虑通过贪心分析去掉式子内部最值符号以简化运算。
-
使用欧拉序将路径问题转化为区间问题。(使用条件:能够通过贪心等方法忽略不在路径上的节点。)
-
线段树维护三点偏序最值问题。
有另一个做法,需要用到一个关于直径的结论:对于两个点集的树上最大直径(权值非负),并集点集的树上最大直径的端点一定是原四个端点中的两个。(太太太太巧妙了这个怎么想到的!!!)于是在线段树上维护,每次合并直径集合即可。
-
-
-
虚树
-
树的直径
求法:
-
树形 DP。边权可正可负。
-
二次 DFS。边权必须非负。
- P2491 [SDOI2011] 消防:树的直径经典题。首先是该路径一定完全在直径上的性质很妙,然后是根据直径最长性我们可以直接扫描去掉无用的单调队列。
-
-
重心
性质:
-
重心的数量最多有两个,如果有两个,则它们相邻。
-
删掉重心后,所有子树大小不超过整棵树的一半。
-
所有点到某个点的距离和中,到重心的距离之和最小,如果有两个重心那么和相同。
-
在删除或加入一个叶子,最多只会引起重心移动一条边。
-
最短路
-
P6190 [NOI Online #1 入门组] 魔法:稍微复杂一些的矩阵乘法题目,需要用到多个矩阵复合。
-
P1266 速度限制:拆点最短路,或者说就是分层图。(当我们觉得一个点的信息单薄时,可以像 DP 一样多加几维。)
-
“可能最短路”问题:
-
通过起点、终点跑几次最短路,做简单数值拼凑。但是需要证明该贪心成立。
-
建出最短路径图,可以证明其一定为 DAG。
-
-
差分约束。可以发现合法的总长一定在一个区间范围内,尝试二分区间上下界。
最难的一步是不合法时,如何判断 \(mid\) 是在合法区间左侧还是在右侧。这里给出的方法是分离参数,维护一个变量 \(k\) 表示负环选了几个 \(mid\),然后我们就可以根据 \(k\) 的正负判断 \(mid\) 应该增大还是缩小。(给出了一种二分某个特定区间的方法。)
实现注意事项:
-
差分约束可以通过与超级源点连无向边得到一个常数值。
-
直接跑差分约束求得的不是真值,只是一个相对大小,要减去超级源点的值才能得到真值。
-
连通性
这类题目一般首先做的就是缩点。
-
P3825 [NOI2017] 游戏:思维很简单的 2-SAT 问题,建图较为恶心。
-
P5058 [ZJOI2004] 嗅探器:我的评价是不如直接建圆方树后枚举路径上所有割点。
-
P7687 [CEOI2005] Critical Network Lines:其实就是一道简单边双连通分量。
-
容易发现我们实际只用保留整个图的连通性,于是可以试着在连通性不变前提下对图做简化(这一点有点像 P5292 [HNOI2019] 校园旅行)。发现根据给定性质,我们可以去掉 \(x \rightarrow z\) 只保留 \(x \rightarrow y\) 以及 \(y \rightarrow z\)。如此,每个点只有一个入度,故形成的结构一定为外向树形图。
考虑加边的影响。一个简洁的做法是建出虚树后拓扑排序或怎么做一下求得路径上所有可能点。
欧拉回路
-
对于有向图,存在欧拉回路要求原图非零度点强连通,且所有点出度等于入度。
-
对于无向图,存在欧拉回路要求原图非零度点连通,且所有点度数为偶数。
-
找到一条欧拉回路可以通过 dfs 搜索,并加入当前弧优化(否则会导致超时)。
-
P3520 [POI2011] SMI-Garbage:如果一个方案中一条边经过了 \(x \ge 2\) 次,则一定存在一个等价方案中它只被经过了 \(x-2\) 次。然后可以去掉 0 边,转化为欧拉回路问题。
DP
DP 难点:
-
-
bool 型 DP 优化方式之将一维限制存入 DP 状态。
-
对一维排序去除限制。
-
我还有一个想法:用生成函数刻画问题,从小到大排序物品,做物品的增删。每个物品最多加入一次、删除一次,故时间复杂度 \(O(nk)\)。(似乎有问题,因为这是 bool 型 DP,或运算没有逆运算。)
-
-
-
显然状压。最暴力的 DP 设法是 \(f[S][i][j]\) 表示选了集合 \(S\) 内的卡片用了 \(i\) 个红宝石 \(j\) 个蓝宝石是否可行。
-
使用经典 bool 型 DP 优化,设 \(f[S][i]\) 表示最少用了几个蓝宝石。
-
优化状态大小。改为设 \(f[S][i]\) 表示省下了 \(i\) 个红宝石。
-
-
-
“组合意义”转化问题:问题可以等价于两个人同时进行操作,结果相同的方案数。
-
我还有一个想法:正难则反,使用 \((\sum_{i=1}^k a_i)^2 = (\sum_{1 \le i \le k} \sum_{1 \le j \le k, i \ne j} a_i a_j) + \sum_{i=1}^k a_i^2\) 转化。(不过这个方法没有本质上的优化,因为需要枚举两个序列第一个不同的位置。)
-
-
暴力:\(f[S][i][j][k]\) 表示选了 \(S\) 集合内的,一共过了 \(i\) 题,前一个选的 \(j\),且 \(b_j = k\)。
然后需要研究一下如何判定一个合法的统计对象,以便我们发现统计对象的性质。接着即可发现费用提前计算。
-
DAG 形成树形图方案数:\(\prod \frac{1}{deg_i}\)。然后再做一次容斥(减去成环部分),并且做一次带系数 DP 即可。
这种计数 DP 的初值设置有讲究。有些时候,权值会被拆成多个 DP,需要我们在末端将它们合并起来;但我们其实可以通过在初值处的设置,做到只用一个 DP 就可以了。比如此题,其实可以将一个 DP 拆为一个统计路径条数的 DP 以及一个统计逆元乘积的 DP。
-
这个 10 显然是用来状压的,问题在如何优雅地状压?
DFS 树的性质:只有返祖边,没有横叉边。于是该问题可以直接被转化为一个关于祖先的树上 DP。
据说此题的实现也大有文章。
-
这题的关键是——建图!然后可以发现等价于最大独立集问题。其实此时就可以用上题相同的方法解决了,不过如果进一步分析图的性质可以发现其为若干网格图,有更简单的状压方法。
二维网格式状压 DP——轮廓线 DP:不是按行转移,而是按格转移,记录轮廓线上的情况。
-
P9676 [ICPC2022 Jinan R] Skills
这道题很棘手的一个地方在于“任何一项技能的熟练度都不可能小于 0”,这导致我们只能写最最暴力的 DP,即记录每一个技能现在的熟练度、以及多少天没有练习了。
于是需要观察发现一个性质:如果某个技能的熟练度被消为 0,则一定不优。这很容易证明。于是就容易列出最简单的 DP:\(f[i][j][x][y]\) 表示这是第 \(i\) 天,前一天选的技能为 \(j\),另外两个技能分别有 \(x, y\) 天没有练习。
然后优化其实就很显然了。如果一个技能 \(x\) 天没有练习,它的贡献减少的量为 \(x(x+1)/2\)。容易想到 \(x, y\) 的上界都并不大,满足 \(\le 2\sqrt{V}\)。
数学
数论
-
素数判定
-
试除法单次 \(O(\sqrt{n})\)。
-
筛法。
-
Miller-Rabin。
-
-
分解质因数
-
试除法单次 \(O(\sqrt{n})\)。
-
筛法。(使用埃氏筛、线性筛预处理都可以做到 \(O(\log n)\) 单次分解。)
-
Pollard-Rho。
-
-
埃氏筛:\(O(n \log \log n)\)
-
gcd & lcm
-
若考虑分解质因数,gcd 可看成对两个数的同一质因子的指数取 min,lcm 可以看成对该指数取 max。
-
\(a \bmod b \le a/2\),这即为辗转相除法复杂度为 \(O(\log n)\) 的原因。
-
若 \(b\) 不为 \(a\) 倍数,则 \(\gcd(a, b) \le a/2\)。
-
若 \(a \ne b\),则 \(\gcd(a, b) \le |a-b|\)。
-
-
欧拉筛
-
枚举每个合数的最小素数来保证线性复杂度。
-
如何帮助分解质因数?每次除以最小质因子,然后递归到商的情况。
-
可以用于维护积性函数在 \(1 \sim n\) 上的取值。如欧拉函数、莫比乌斯函数、约数个数、自然数幂等。
-
-
欧拉函数
-
\(\varphi(n)\) 表示 \(1 \sim n\) 中与 \(n\) 互质的数的个数。
-
若 \(n = \prod p_i^{c_i}\),则 \(\varphi(n) = n \prod (1 - 1/p_i)\)。
-
积性函数。
-
P2568 GCD:枚举每个素数作为 \(\gcd\)。
-
-
逆元
-
模数为素数:\(a\) 的逆元为 \(a^{p-2}\)。
-
模数不为素数:等价于求解方程 \(a a^{-1} + my = 1\),有解当且仅当 \(a, m\) 互质。
-
模数不为素数且 \(a, m\) 不一定互质:假如需要求解的是 \((x/a) \bmod m\),我们先对 \(m\) 分解质因数,然后将 \(x, a\) 与 \(m\) 的公共质因子以及去除公共质因子的部分分开维护。e.g. P4588 [TJOI2018] 数学计算
-
-
扩展欧几里得算法
-
用于求解方程 \(ax + by = \gcd(a, b)\)。故 \(ax + by = k\) 有解当且仅当 \(\gcd(a, b) | k\)。
-
\(|x|, |y| \le \max(a, b)\)。
-
-
扩展中国剩余定理
合并两个同余方程。若有 \(x \equiv a \pmod{m_1}, x \equiv b \pmod{m_2}\),则 \(x=a+k1*m1=b+k2*m2 \rightarrow k1*m1-k2*m2 = b-a\),使用 exgcd 求解即可。
- P8457 「SWTR-8」幂塔方程(只有质数模数的版本):非常智慧。构造 \(x \equiv n \pmod{p}, x \equiv 1 \pmod{p-1}\),可以证明一定有解。
组合
-
组合数
-
\(\dbinom{n}{m} = \dfrac{n!}{m!(n-m)!}\)
-
二项式定理:\((x+y)^n = \sum_{k=0}^n \dbinom{n}{k} x^k y^{n-k}\)。
-
Lucas 定理:\(\dbinom{n}{m} \equiv \dbinom{n / p}{m / p} * \dbinom{n \bmod p}{m \bmod p} \pmod{p}\)(模数为质数)。
-
插板法:n 件物品排成一列,分段允许空,分成 m 段的分割方案为 \(\dbinom{n+m-1}{m-1}\);如果是插入 \(m\) 个隔板的话,方案数为 \(dbinom{n+m}{m}\)。
-
-
数列
-
第一类斯特林数:将 \(n\) 个有标号物品分为 \(m\) 个无标号圆排列:\(S_1(n,m) = S_1(n-1,m-1) + S_1(n-1, m)*(n-1)\)。
-
第二类斯特林数:将 \(n\) 个有标号物品分为 \(m\) 个无标号组且组非空:\(S_2(n,m) = S_2(n-1,m-1) + S_2(n-1,m) * m\)。
-
错排数:\(D(n) = (n-1)(D(n-1) + D(n-2)) = n*D(n-1)+(-1)^n\)。
-
卡特兰数(使用折线容斥证明):\(C_n = \dbinom{2n}{n} - \dbinom{2n}{n-1}\)。
-
-
容斥 & 二项式反演
设 \(f(k)\) 表示在 \(n\) 个条件中钦定 \(k\) 个条件必须满足、其它任选的方案数,\(g(k)\) 表示恰好满足 \(k\) 个条件的方案数,则有:
\[f(i) = \sum_{k=i}^n \dbinom{k}{i} g(k) \]\[g(i) = \sum_{k=i}^n (-1)^{k-i} \dbinom{k}{i} f(k) \]下面这个是二项式反演的另一种形式。设 \(f(k)\) 表示。。。?搞不懂等下再来:
\[f(k) = \sum_{i=0}^k \dbinom{k}{i} g(i) \]\[g(k) = \sum_{i=0}^k (-1)^{k+i} \dbinom{k}{i} f(i) \] -
推式子技巧
-
例题
-
[ABC266G] Yet Another RGB Sequence:插板法 + (二项式反演)。多层插板法(或者说多重组合数,但使用插板法这种理解方式有时会更加自然)可以合并多个不同元素的序列。
-
[ARC102E] Stop. Otherwise...:二项式反演。。。
-
挖掘题目的突破口
感觉“优化”这个稍微有点太宽泛了。。。
-
以前某次模拟赛似乎出过原题:link。这道题最难的突破点在于由于题目限制是完全立方数,联想到质因数分解只用枚举到立方根,然后尝试进行后面的分类讨论。 -
CF GYM 105336 CCPC 2024 预选赛 I 找行李
巧妙题。两个关键:
-
考虑直接枚举时间 \(t\)。如何想到?你观察统计对象,有这么几个关键词:“第一个人”“花费时间”。显然枚举第一个人是不方便的,然后看到数据范围也不大,于是考虑以花费时间为统计对象。
-
“恰好”转化为“至少 / 至多”。
-
-
Combination Lock:听懂了,太妙了!
-
U285763 優しさの記憶:似乎特别麻烦。还需要 DP 套 DP。