sol all in one
1. P9989(势能线段树)
视 \(n, q\) 同阶。
一次有效的取 \(\gcd\) 会将原数减半,因此拿区间 \(\operatorname{lcm}\) 维护区间是否可以进行有效的取 \(\gcd\) 即可。由于是势能线段树,时间复杂度 \(O(n \log^2 V)\)。
2. 广东集训 D1T0(Ad-hoc)
设 \(p_i\) 表示满足 \(a_j = i\) 的 \(j\),即值为 \(i\) 的数在 \(a\) 中的位置。那么显然有 \(p_{a_i} = i\)。
考虑用 \(p\) 数组来刻画操作。一次操作在原数组中是将一个子序列挪到了最前面。这样,这个子序列中每个数的位置变成了 \(1, 2, 3, 4,\dots\),而剩下数的位置则变成了 \(n, n - 1, n - 2, \dots\)。形式化地,如果我们选中了 \(a\) 的一个子序列 \(A\)(\(A\) 记录的是子序列在 \(p\) 中的下标),记剩下的为 \(B\),那么操作后 \(p\) 的值如下:
- \(A\) 中,这相当于把 \(p_i\) 以 \(p_i\) 的大小为关键字赋值为 \(1, 2, 3, 4 \cdots\),也就是将 \(p_i\) 离散化了。
- \(B\) 中,也是离散化,但是总体要加上一个值,因为 \(B\) 在后面,所以 \(B\) 中所有元素的 \(p\) 值都是大于 \(A\) 中的。
举个例子:\(p = [1,3,2,4], A =[1, 2], B = [3, 4]\),那么操作完 \(p\) 就变为了 \([1,2,3,4]\)。
我们的目标是将 \(p\) 变为 \(1, 2, 3, 4, \dots ,n - 1, n\)。考虑将 \(p\) 分割成若干个上升的连续子序列,记为 \(K\) 数组。我们发现,对于一个位置 \(i\),如果我们令 \(K_i \subseteq A,K_{i + 1} \subseteq B\),那么经过操作后 \(K_{i + 1}\) 内的所有值都比 \(K_i\) 大,所以它们可以合并成一个新的上升的连续子序列。这样,我们把满足 \(i \bmod 2 = 1\) 的 \(K_i\) 都归于 \(A\),其他的归于 \(B\),这样操作后我们合并了所有像上面一样相邻的子序列,子序列总数减半。所以,一次操作可以将子序列总数减半,直到只剩一个就结束,这样合并次数是 \(O(\log n)\) 级别的,是最少的。
输出方案的话按照上述过程模拟即可,注意一些细节。时间复杂度 \(O(n \log n)\),不明白数据为什么这么小。
3. 广东集训 D4T2 / ARC066D(斜率优化,分治)
如果一个区间的权值会且仅会对区间内的点的答案产生贡献,在容易求出区间权值的情况下,可以考虑分治求解点的答案。
视 \(n, q\) 同阶。
首先,题意转化为求若干个连续段的答案。
考虑 DP。设 \(f_i\) 表示考虑前 \(i\) 个数,强制不选 \(i\) 时最大的价值和。显然,有:
同理,设 \(g_i\) 表示考虑后 \(i\) 个数,强制不选 \(i\) 时最大的价值和。转移类似。
直接求时间复杂度是 \(O(n ^ 2)\) 的。但这显然是可以斜率优化的形式,可以通过维护单调栈做到 \(O(n)\)。但是这有些细节需要考虑,所以我采用了李超线段树来维护。
考虑修改操作。显然,如果不选 \(x\),那么答案就为 \(f_x + g_x\)。如果选 \(x\),那么我们需要维护一个 \(h_i\),表示强制选 \(i\) 时最大的价值。这样,选 \(x\) 时的答案就是 \(h_x + a_x - y\)。下面我们考虑如何求出 \(h_i\)。
显然,有如下的转移式:
直接求时间复杂度是 \(O(n ^ 3)\) 的,有待优化。
考虑分治。设当前分治到的区间为 \([L, R]\),区间中点为 \(\text{mid}\)。现在我们需要求转移式中 \([l, r] \subseteq [L, R]\) 时区间 \([l, r]\) 对 \(h_i\) 的贡献。\([l, r] \subseteq [L, \text{mid}]\) 或 \([l, r] \subseteq [\text{mid} + 1, R]\) 的情况可以递归计算,所以我们只需要考虑 \(l \in [L, \text{mid}]\) 且 \(r \in [\text{mid} + 1, R]\) 的情况。
考虑求出区间 \([l, r]\) 对 \(i \in [L, \text{mid}]\) 的 \(h_i\) 的贡献。设 \(p_j\) 表示 \(l = j, r \in [\text{mid} + 1, R]\) 时最大的价值和,那么显然 \(p_j\) 可以贡献到 \(i \in [j + 1, \text{mid}]\) 的 \(h_i\),且对于 \(p_j\) 有:
这也是一个可以斜率优化的式子,那么李超线段树即可。
由主定理,总时间复杂度为 \(O(n \log^2 n)\),常数小,可以通过。
4. P12196(线段树优化 DP)
当约束条件过强时,可以考虑弱化约束条件。这里的“弱化”需要保证弱化之后的答案和弱化前的答案相同。
视 \(h, w\) 同阶。
首先,设操作后被覆盖的 \(t\) 个区间为 \([L_1, R_1], [L_2, R_2],\dots ,[L_t, R_t]\)。那么我们需要满足:
- 操作后,所有滑墙拼起来恰好以形成这些区间。
- 被解锁的区间的价值和小于等于 \(k\)。
这样不好做,考虑弱化问题。这里说一下弱化问题的基本方法。对于满足 \(p\) 性质的元素集合 \(P\),如果我们要把它弱化为满足 \(q\) 性质的元素集合 \(Q\)(显然有 \(P \subset Q\)),需要满足对于任意的 \(x \in Q\) 都存在一个 \(y \in P\) 使得 \(y\) 优于 \(x\)。具体来说,在这道题中,我们可以把第一个条件弱化为“操作后,所有滑墙均为这些区间的子区间”。这样,问题就变得简单了起来。
考虑通过式子去刻画这些条件。显然,对于滑墙 \([l_i, r_i]\),如果存在一个 \(j\),满足 \([l_i, r_i] \subseteq [L_j, R_j]\),那么这个滑墙一定不用解锁,否则一定要解锁。同时,如果最长的滑墙的长度大于这 \(t\) 个区间中任意一个的长度,那么它无法被放下,这样是不行的。综上,这些条件等价于:
- \(\sum\limits_{i = 1}^h c_i - \sum\limits_{i = 1}^h c_i \sum\limits_{j = 1}^t [[l_i, r_i] \subseteq [L_j, R_j]] \leq k\)。
- \(\max\limits_{i = 1}^h \{ r_i - l_i + 1 \} \leq \max\limits_{i = 1}^t \{ R_i - L_i + 1 \}\)。
考虑 DP。设 \(f_{i, j, k}\) 表示考虑前 \(i\) 个位置,没被覆盖的位置有 \(j\) 个,上面的第二条限制满足情况为 \(k\) 时所有不用解锁的滑墙的价值和的最大值。转移时分讨第 \(i\) 个位置的覆盖情况,有:
- \(i\) 被覆盖,则有 \(f_{i, j, k} \gets f_{i - 1, j - 1, k}\)。
- \(i\) 没被覆盖,枚举转移点 \(x\),记最长的滑墙的长度为 \(d\),则有 \(f_{i, j, k \land [d \leq i - x]} \gets f_{x, j, k} + \sum\limits_{p = 1}^h c_p [[l_p, r_p] \subseteq [x + 1, i]]\)。
直接做的话时间复杂度为 \(O(h w^2)\),显然不能通过。
考虑优化。首先,我们转移的瓶颈在于第二个 DP 式子。考虑使用 经典套路,将区间 \([l_p, r_p]\) 的贡献挂在 \(r_p\) 上,再用线段树维护即可。时间复杂度 \(O(w^2 \log w)\)。
5. QOJ 6807(随机化)
将每个随机黑白染色,统计答案时我们只考虑统计左半部分为黑色,右半部分为白色的环。由于 \(k \leq 10\),一次被统计到的概率为 \(\frac{k}{2^k} \approx \frac{1}{100}\),所以我们随机 \(1000\) 次就足够了。这样,我们只需要统计那些特点的环即可。这是容易的,因为 \(k \leq 10\),所以这个环由一个长度最多为 \(4\) 的黑链和一个长度最多为 \(4\) 的白链构成。那么,我们只需要统计长度小于等于 \(4\) 的链的答案,再枚举黑白分界点来组合这些链即可。除去随机化的部分,时间复杂度 \(O(m ^ 2)\)。
6. 广东集训 D7T2(阈值分治)
先考虑证明一个结论:从点 \(x\) 出发能到达的点的数量是 \(O(\sqrt{x})\) 个。
设 \(f(x, y)\) 表示从点 \(x\) 出发,终点编号大于等于 \(y\) 时能到达的点的个数。显然,走一步点的编号期望会除二,也就是说搜索树的深度约为 \(\log \frac{x}{y}\)。又由于搜索树是二叉树,所以 \(f(x, y) = O(2^{\log \frac{x}{y}}) = O(\frac{x}{y})\)。这样,\(x\) 能到达的点的数量最多为 \(f(x, \sqrt{x}) + \sqrt{x} = O(\sqrt{x})\) 个,结论得证。
有了这个结论之后就好作多了。考虑阈值分治,设阈值为 \(B\)。
首先,我们可以先预处理出两个点编号都小于 \(B\) 时它们的距离。这部分显然可以 \(O(B^{1.5})\) 完成。如果两个数编号中有一个大于等于 \(B\),那么我们就暴力 bfs,直到编号小于 \(B\)。这里最坏要走 \(f(x, B) = O(\frac{x}{B})\) 次,那么单次查询的时间复杂度即为 \(O(\frac{n}{B})\),查询总时间复杂度为 \(O(\frac{n^2}{B})\)。
这样,本题总时间复杂度为 \(O(B^{1.5} + \frac{n^2}{B})\),在 \(B = n^{0.8}\) 时最优,为 \(O(n^{1.2})\)。需要注意的是这里空间复杂度为 \(O(n^{1.6})\),某些数组需要开 short 存储。实测在 \(B = 2 \times {10}^4\) 时空间才不会爆,至于时间的话就需要卡卡常了,但反正我卡过了 qwq。
其实,也可以离线做。这样时空复杂度都更优一些。
7. P11831(bitset,分块)
在最后分析复杂度时,视 \(n, m, q\) 同阶。
首先先跑一个 DAG 可达性,这部分的时间复杂度为 \(O(\frac{n^2}{\omega})\)。
先考虑不带修怎么做。考虑对 \(a\) 数组进行值域分块,设块长为 \(B_1\)。我们需要预处理 \(f_{u, i}\) 表示最大的 \(b_v\),满足 \(u\) 可达 \(v\) 且 \(a_v\) 属于第 \(i\) 个块。这显然可以拓扑排序求解,这部分的时间复杂度为 \(O(\frac{nm}{B_1})\)。
求出 \(f\) 数组之后,对于每次查询的区间 \([l, r]\) 而言,整块的答案就通过 \(f\) 数组来求,而散块就直接暴力枚举,枚举时判断每个值对应的点能否到达即可。这部分的时间复杂度为 \(O(q(\frac{n}{B_1} + B_1))\),不带修时总时间复杂度为 \(O(\frac{n^2}{\omega} + \frac{nm}{B_1} + q(\frac{n}{B_1} + B_1))\)。
考虑带修怎么做。考虑操作分块,设块长为 \(B_2\)。对于一个块,我们给块内有修改操作涉及到点打上标记。显然,标记的点的个数是 \(O(B_2)\) 个。每次块前重构时,我们重新计算 \(f\) 数组,但只针对没打标记的点。对于一次询问,没打标记的点的答案可以通过不带修时的方法求出,而打了标记的点的答案可以通过暴力枚举来求出。这样,单次重构的时间复杂度为 \(O(\frac{nm}{B_1})\),单次查询的时间复杂度为 \(O(\frac{n}{B_1} + B_1 + B_2)\),总时间复杂度为 \(O(\frac{n^2}{\omega} + \frac{nmq}{B_1 B_2} + q(\frac{n}{B_1} + B_1 + B_2)))\)。
取 \(B_1 = B_2 = n^{\frac{2}{3}}\),这时总时间复杂度为 \(O(\frac{n^2}{\omega} + n^{\frac{5}{3}})\)。实测取 \(B_1 = B_2 = 2475\) 时可以极限卡过。
8. P7481(递推)
显然,直接求并没有什么好的方法。考虑通过构造递推式来求解。
注意到:
上式容易通过以下组合恒等式证明:
这样,我们有:
变形一下,有:
需要预处理 \(a = 0\) 或 \(b = 0\) 时的情况,组合数暴力算就行。时间复杂度 \(O(m^2)\)。
9. P4389(多项式,ln 与 exp)
通过 ln 与 exp 可以轻松实现乘法与加法之间的相互转化。
先写出答案的 OGF,为:
乘积不好计算,先 ln 再 exp 转化为求和,即为求下式的 exp:
注意到:
事实上,后面是一个枚举 \(V\) 倍数的形式,那么我们对于每个 \(V\) 的类都枚举倍数,再给对应位置加上系数即可。这部分的时间复杂度为 \(O(n \log n)\),而一次 exp 也可以做到 \(O(n \log n)\),所以总时间复杂度为 \(O(n \log n)\)。
注:对于最后一个式子,两边求导即证。
10. 广东集训 D1T1 / QOJ 4415(状压 DP)
并查集的哈希:先拆分成联通块,再对联通块哈希,最后再异或起来。
考虑将一条边按如下方式拆成两条边:
- \(a < b\),拆成 \((u, v, a, 1)\) 和 \((u, v, b, 0)\)。
- 否则,拆成 \((u, v, b, -1)\) 和 \((u, v, a, 0)\)。
考虑模拟 Kruskal 算法的过程。先将边权从小到大排序,之后考虑加边:
- \((u, v, w, 1)\),可选可不选,加到最小生成树里则增加权值 \(w\),同时增加一条 \(a\) 类边。
- \((u, v, w, -1)\),同上,只不过加了之后减少一条 \(a\) 类边。
- \((u, v, w, 0)\),它的选法已经确定,因为拆分出的另一条边已经被考虑过。
这样,我们设 \(f_{i, S, j}\) 表示考虑前 \(i\) 条边,联通情况为 \(S\),选了 \(j\) 条 \(a\) 类边时的答案。这里的“联通情况”其实相当于一个并查集,处理方法前面写了。转移时分讨是否选边和是否加入最小生成树即可,这些都是容易做的。
时间复杂度 \(O(m ^ 2 \operatorname{Bell}(n))\),其中 \(\operatorname{Bell}(n) \leq 21147\),可过。
11. 广东集训 D1T2 / Gym 103428C(模意义下 01 背包)
模意义下 01 背包是有双 log 做法的!!!
视 \(n, p\) 同阶。
首先,利用原根 \(g\) 将 \(x\) 变为 \(g^y\) 形式,那么题目转化为一个模 \(p - 1\) 意义下的 01 背包问题。
下文中,所有加减法运算均在模 \(p - 1\) 意义下进行。
显然,有 \(f_i \gets f_i \lor f_{i-x}\)。考查数对 \((f_i, f_{i + x})\),它会对 \(f_{i + x}\) 造成影响。显然,当数对为 \((1, 0)\) 时,\(f_{i+x}\) 的值才会在转移后发生改变。那么我们只需要找出所有这样的 \((1, 0)\) 数对即可。这不好做,放宽一下限制,我们只需要找出所有 \((1, 0)\) 数对和 \((0, 1)\) 数对,即所有满足 \(f_{i} \neq f_{i+x}\) 的 \(i\) 即可。转移时需要特判一下 \((0, 1)\) 的情况,这时候就不转移。
怎么找?假设当前从 \(i\) 开始找,二分一个 \(k\),利用哈希值判断 \(f_{i} \sim f_{i + k}\) 是否与 \(f_{i+x} \sim f_{i+x+k}\) 完全相同,这样可以找到第一个满足它们不相同的 \(k\),此时 \(i+k\) 即为满足 \(f_{i+k} \neq f_{i + k + x}\) 的位置,记录后就跳到 \(i + k\),即 \(i \gets i + k\),然后继续重复上述过程。这样做的总时间复杂度为 \(O(\log^2 n)\),因为需要拿树状数组维护哈希值。
由于每次有效的转移都会将 \(f\) 中 \(0\) 的数量减一,又因为 \((1, 0)\) 数对的数量与 \((0, 1)\) 数对的数量是一样的,所以势能就是对的。总时间复杂度为 \(O(n \log ^2 n)\),这个做法似乎很劣,跑的很慢。

浙公网安备 33010602011771号