算法竞赛复健记录
高三学了一年文化课感觉已经不会算法竞赛了,开个博客记录一下复健历程。
CF1662F
题意:有 \(n \le 200000\) 个点,每个点有能量 \(p_i\),消息能从 \(i\) 传到 \(j\) 当且仅当 \(|i - j| \le \min(p_i, p_j)\),求消息从 \(a\) 点传到 \(b\) 点至少需要经过几个点。
考虑把点按 \(p_i\) 降序依次插入某个数据结构 \(S\)。当 \(i\) 要插入的时候,将 \(i\) 和 \(\{j \mid j \in S \land i - p_i \le j \le i + p_i\}\) 里的点连边。这里选择可持久化线段树作为 \(S\),用线段树优化建图的方式连边。时空复杂度是 \(O(n \log n)\),但是由于实现得不好 MLE 了。
题解的一种做法比较巧妙,直接用线段树优化了 BFS 的过程。本题相当于每条边长度是 1,所以 BFS 时让每个点只访问一遍就可以保证正确性和时间复杂度。对当前 BFS 到的点 \(i\),它能走到 \(j\) 当且仅当 \(j \in [i - p_i, i) \land i \le j + p_j\),或者 \(j \in (i, i + p_i] \land i \ge j - p_j\)。对前一情况,用一个支持单点修改、查询区间最大值的线段树 \(S\),令每个 \(S_j = j + p_j\),当从 \(i\) 能走到 \(j\) 时,即 \(S.\max[i - p_i, i) \ge i\),就把 \(j = S.\operatorname{argmax}[i - p_i, i)\) 从线段树里删去,并将 \(j\) 加入 BFS 的队列。后一情况同理。这样的时间复杂度是 \(O(n \log n)\),空间复杂度是 \(O(n)\),常数也小,不会 MLE。
CF1765J
题意:有一个 \(n \times n\) 的矩阵 \(c\),其中 \(c_{i,j} = |a_i - b_j|\),有两种操作:
- 将矩阵一行或一列的的元素减去(加上)\(x\),代价为 \(x\)(\(-x\))
- 将矩阵一个位置的元素减去 \(x\),代价为 \(x\),要求 \(x>0\)
求将该矩阵所有元素变成 0 所需的最小代价。
设第 \(i\) 行做了 \(p_i\) 的操作,第 \(j\) 列做了 \(q_j\) 的操作(\(p_i, q_j \in \mathbb R\))。我们希望最大化 \(\sum_{i=1}^n (p_i + q_i)\),因为操作 1 显然比操作 2 更优。这可以写成一个线性规划问题:
考虑其对偶问题:
可以只考虑 \(\lambda_{i,j} \in \{0, 1\}\) 的情况。这相当于从 \(c\) 的每行每列选恰好一个元素,即选出一个排列 \(p\),使得 \(\sum_i c_{i, p_i} = \sum_i |a_i - b_{p_i}|\) 最小。显然选择 \(a,b\) 同序的排列最优(即,选择的排列满足 \(a_i \le a_j \iff b_{p_i} \le b_{p_j}\)),那么将 \(a, b\) 排序即可。
QOJ1643
写成线性规划问题如下:
转为对偶问题:
设 \(f_k(x)\) 表示当 \(c_k = x\) 时 \(\sum_{i=1}^k |c_i|\) 的最小值,那么有转移
再令 \(g_k(x) = f_k(x - \sum_{i=1}^{k-1})\),有转移
\(g\) 的转移可以用 slope trick 优化,时间复杂度 \(O(n \log n)\)
值得一提的是,直接在原问题上转移会得到一模一样的做法,见:https://codeforces.com/blog/entry/90956?#comment-794463
CF GYM 103107 I
博弈论
参考 Sprague–Grundy theorem 和 Nimber
- 公平组合游戏等价于(等价的定义见上述链接)一个 Nim 游戏
- Nim 游戏中,状态 \(x\) 可以到达的状态集合为 \(S\),则 \(\newcommand\SG{\operatorname{SG}}\SG(x) = \operatorname{mex}\{\SG(y) : y \in S\}\)
- Nim-和(相当于对 Nim 游戏做笛卡尔积?)具有交换律结合律。
\(a + b = \{a' + b : a' \in a\} \cup \{a + b' : b' \in b\}\)
\(\SG(a + b) = \operatorname{mex}\left(\{\SG(a' + b) : a' < a\} \cup \{\SG(a + b') : b' < b\}\right) = \SG(a) \operatorname{xor} \SG(b)\)
平衡树
做题的时候需要写一种平衡树,支持比较两个元素的序关系,于是想到了后缀平衡树用的那种将节点映射到实数上的写法,可是已经看不懂自己写的代码了...
现在看懂了,当时用 fhq-treap 实现的,为了维护元素到实数的映射,插入元素的时候不能 split 再 merge,而是要找到插入的位置,然后把那个子树的映射重构。这样做的复杂度期望是 \(O(n \log n)\) 的,因为每个元素只会被其祖先重构,而 treap 的树高是由随机性保证 \(O(\log n)\) 的。
Tarjan 算法求强连通分量
更新 low
的时候注意:搜索到横叉边的时候,如果点在栈中则要更新 low
,这里的栈不是 dfs 搜索栈,而是维护所有 还未确定所属强连通分量的点 的栈。
线性规划
- https://codeforces.com/blog/entry/105049
- https://www.cnblogs.com/p-b-p-b/p/14932601.html
- https://rusunoi.github.io/books/National-Team-Thesis/2021.pdf
Lagrange 对偶
最优化问题
其中 \(f: X \to \mathbb{R}\) 是目标函数,\(g: X \to \mathbb{R}^c\) 是约束函数。该问题的拉格朗日对偶是:
注意到有 \(t(\lambda) = \min_{x \in X} [f(x) - \lambda^\top g(x)] \le f(x)\),因此 \(t(\lambda)\) 给出了 \(f(x)\) 的下界,而对偶实则是最大化该下界。
线性规划对偶
线段树上二分
见 ac-library 及其 文档,写法是 zkw 线段树。
二分图匹配
Theorem (Hall's marriage theorem). A bipartite graph with \(n\) vertices on both sides has a perfect matching if and only if for every subset \(S\) of vertices on the left side, \(|N(S)|≥|S|\). Here, \(N(S)\) is the set of vertices on the right side that have an edge to one of the vertices of \(S\).
最大点权匹配:求二分图的带权最大匹配,其中只有一侧点有点权。做法:将点按点权排序,贪心地依次尝试增广(使用匈牙利)。
最大边权完美匹配:设 \(x_i\) 表示第 \(i\) 条边是否在匹配中,\(c_i\) 表示边权。相当于求 \(\sum x_ic_i\) 的最大值(在 \(x\) 构成匹配的约束下),写成对偶问题为求 \(\sum h_i\) 的最小值,其中 \(h_u + h_v \ge c_e \forall e = (u, v)\),这里的 \(h\) 称为顶标。根据互补松弛定理,\(h_u + h_v = c_e \Leftarrow x_e = 1\).
QOJ9372
记 \(S\) 的前缀函数为 \(\pi\),记 \(\Pi_i = \{\pi^k(i) : k \in \mathbb N\}\),其中 \(\pi^0(i)=i,\pi^{k+1}(i)=\pi(\pi^k(i))\)。所求为 $$\sum_{i=1}^n A_i\sum_{j \in \Pi_i-0}B_{i-j+1}$$
设 \(f_i=\sum_{j \in \Pi_i-0}B_{i-j+1}\),考虑如何转移。
注意到 \(\Pi_i=\{0\} \cup C\),其中 \(C \subseteq \{x+1:x\in \Pi_{i-1}\}\)。我们记 \(F_i=\{x+1 : x \in \Pi_{i-1}\} \backslash C\),那么 \(f_i=f_{i-1}-\sum_{x \in F_i} B_{i - x + 1}\)。
更进一步,若 \(\pi(i) = \pi^k(i-1)+1\), 那么 \(\Pi_i=\{0\} \cup T_i\),其中 \(T_i \subseteq R_i = \{\pi^r(i-1) + 1 : r \in \mathbb N \land r \le k\}\)。现在考虑 \(R_i \backslash T_i\),可以发现 \(R_i \backslash T_i = F_{\pi_i}\)。于是我们就得到了 \(F_i=\{\pi^r(i-1)+1 : r > k\} \cup F_{\pi_i}\)。
按上述方法维护 \(F_i\),复杂度应为 \(\sum_{i=1}^n |F_i|\)。注意到 \(|F_i| \le |\Pi_{i-1}|-|\Pi_i|+1\),因此 \(\sum_{i=1}^n |F_i| \le n\)。