UOJ Logo EntropyIncreaser的博客

博客

2020年北京青少年信息学科普日市级活动(初中组)题解

2020-11-05 13:06:37 By EntropyIncreaser

T1 生命游戏(gol)

注意到只有 $2^{4\times 4}$ 种输入,我们可以先枚举每种输入确立其下一个状态,然后倍增或者通过内向树处理都是可以的。

时间复杂度 $\Theta(states\log T + Q\log T)$ 或者 $\Theta(states + Q)$。

T2 装备升级(upgrade)

性质 A

我们容易发现这样一个事实:不可能有两个装备恰好是一级,否则一定可以将一个升级挪到另一个上。

因此可以导出这一性质:

  • 若 $m$ 是偶数,那么一定是选 $w_1 + w_2$ 前 $m/2$ 小的。

  • 若 $m$ 是奇数,那么要么是选前 $\lfloor m/2\rfloor$ 小的 $w_1+w_2$,再在剩下的里面选一个一级,要么是选前 $\lceil m/2\rceil$ 小的 $w_1+w_2$,再在这些里面退掉一个二级。

因此我们先按照 $w_1+w_2$ 排序,然后枚举就可以了。

性质 B

我们考虑贪心选取最小的 $m$ 个 $w$,不难发现他们一定是可以满足要求的。因此只需一次 $\texttt{nth_element}$,可以做到 $\Theta(n)$。

std

我们考虑 $w_1\le w_2$ 和 $w_1>w_2$ 已经涵盖了所有可能性,我们考虑对于两拨分别算出所有 $m$ 的答案,之后枚举一边实际选了几个就可以了。

对于性质 A,我们可以用前后缀 $\min$ 来加速,对于性质 B,我们只需要排序之后做一遍前缀和即可。

因此总共复杂度瓶颈为排序,$\Theta(sort(n))$。

T3 因子统计(divisor)

我们注意到如果一个数的质因子分解是 $p_1^{k_1} \cdots p_n^{k_n}$,那么因子数量就是 $(k_1+1)\cdots (k_n+1)$。

std

考虑一个莫队,我们在 $(n,m)$ 的移动过程中维护 $\binom n m$ 的质因子分解中 $> \sqrt n$ 的那部分,这样每次移动只会有 $\Theta(1)$ 个质因子增减。最后统一统计 $\le \sqrt n$ 的质因子贡献,因为总共只有 $\pi(\sqrt n) = \Theta \left( \frac{\sqrt n}{\log n}\right)$ 个质数,我们每个质数暴力做就是 $\Theta(\sqrt n)$ 的了。复杂度是 $\Theta(q\sqrt n + n\sqrt q)$。

bonus

本题可以做到……的复杂度?

BJ United Round #4 (Mirror of Haidian 2019)题解

2019-09-21 12:46:51 By EntropyIncreaser

石子移动

考虑将操作过程倒过来,最后值域在 $[l, r]$ 内的在上一步应该也是一个区间,便可得出最初的区间限制。

时间复杂度 $\Theta(n + q)$。

分数排序

$n$ 阶 Farey 数列可以直接在 stern-brocot 树上遍历:初始设置 $\frac ab = \frac 01, \frac xy = \frac 11$。那么 $(\frac ab, \frac xy)$ 这一节点的左子和右子分别为 $(\frac ab, \frac {a+x}{b+y}), (\frac{a+x}{b+y}, \frac xy)$。不难证明任何出现的一个分数的分子分母都是互质的,且这样可以遍历所有分数。

考虑如何在这个树上找到一个分数,虽然一个节点到根的距离可以是 $\Theta(n)$ 的(例如 $\frac 1n$),但是假设我们走到 $(\frac ab, \frac{a+x}{b+y})$ 叫做 $L$ 操作,否则叫做 $R$ 操作,那么一个节点对应找到它的操作序列的连续段数应该是 $\mathcal O(\log n)$ 的,因为一次 $LR$ 会使得分母倍增。我们可以倍增看操作序列一共下一组 $L$ 或者 $R$ 有多长,因此我们会进行 $\mathcal O(\log^2 n)$ 次 check 操作。

考虑如何计算一次 check: $$ \begin{eqnarray} & &\sum_{x\le n} \sum_{y\le n} \left[\frac xy \le \frac ab\right] [\gcd(x, y) = 1]\\ &= & \sum_{x\le n} \sum_{y\le n} \left[\frac xy \le \frac ab\right] \sum_{d | (x, y)} \mu(d)\\ &= & \sum_d \mu(d) \sum_{y\le \frac nd} \sum_{x\le \frac nd} \left[x \le \frac {ay}b\right] \\ &= & \sum_d \mu(d) \sum_{y = 1}^{\left\lfloor\frac nd\right\rfloor} \min\left( \left\lfloor \frac nd \right\rfloor, \left\lfloor \frac{ay}b \right\rfloor \right) \end{eqnarray} $$ 对 $\left\lfloor\frac nd\right\rfloor$ 除法分块,我们需要 $\mu(d)$ 在这些位置的前缀和,而 $\sum_{y = 1}^{\left\lfloor\frac nd\right\rfloor} \min\left( \left\lfloor \frac nd \right\rfloor, \left\lfloor \frac{ay}b \right\rfloor \right)$,这个式子在一个前缀中取得右边是较小值,这部分我们可以通过类欧几里得算法在 $\Theta(\log b)$ 时间内计算。因此我们一次 check 的时间消耗为 $\Theta(\sqrt n \log n)$。注意前缀和的处理可以使用 $\Theta(n^{\frac 23})$,但是在本题限制下 $\Theta(n)$ 也是 ok 的。

综上所述,时间复杂度为 $\Theta(n^{\frac 23}) + \Theta(\sqrt n \log^3 n)$。

彼岸蔷薇

newbiedmy 这家伙会乱搞,很有毒!

关于背景

那段歌词摘自遗忘山丘

算法一

考虑枚举所有路径,对于每个情况按照定义求出最大路径数。

显然最大路径数可以通过树形 dp 进行计算。$f(u)$ 表示考虑所有两端点均在 $u$ 的子树的路径,此时的最大答案。此时分成两类转移情况讨论,要么选择的路径均不经过 $u$,那么这一情况的最优解即 $u$ 的所有直系孩子的 $f$ 之和。否则枚举选取哪个路径,这一情况的贡献是 $1$ 加上与这条路径相邻的所有节点的 $f$ 之和。

时间复杂度 $\Theta(mn^3)$,预计得分 $32\%$。

算法二

考虑优化 dp 的效率,我们记 $g(u)$ 为 $f(u)$ 减去其所有直系孩子的 $f$,则不难发现 $g(u)$ 对于路径情况的转移就是所选路径上 $g$ 的和再加 $1$。通过一个树状数组即可维护。

时间复杂度 $\Theta((n + m\log n)n^2)$,预计得分 $40\% \sim 44\%$。

算法三

我们对于计算最大路径数有两种不同角度的观察:

  1. 显然 $g$ 的值要么为 $0$ 要么为 $1$,这表示事实上 $g$ 就是代表达到子树中最优解是否必须有一条路径穿过 $u$,这当且仅当存在一条路径经过的 $g$ 均为 $0$。
  2. 这也可以看成一个贪心,我们仅仅是按照路径的 lca 自底向上贪心,能放就放。

考虑通过这种贪心思路来计算解,我们只需在 dfs 的过程中额外维护一个 vis 数组,表示“这个节点是否上面有一个父亲已经作为某条被选择路径的 lca”。未被染色的节点显然时刻都是与根节点相连的一个连通块。我们每次选择一条路径,只需把该节点子树中与之相邻的未被染色的节点染色。检查一条路径的时候,只需检查两端点是否均未被染色。这样就可以做到线性。

时间复杂度 $\Theta((n + m)n^2)$,预计得分 $44\%$。

算法四

我们提出如下观察:假设一个节点 $u$ 满足加入路径 $(u,u)$ 能够使得最大路径数增加,则称节点 $u$ 是 ok 的。那么一条路径加入后能使最大路径数增加,当且仅当路径上的节点都是 ok 的。

之前的过程中我们对于根的选择是任意性的,接下来我们考虑以一个节点为根的时候,穿过它的路径如果能够增大最大路径数,此时显然等价于路径上的节点均未被选中,也就是在贪心过程中这条路径就可以被选。

我们考虑染色之后与根节点联通的未被染色的连通块,这应当与与 $u$ 相邻的 ok 的节点所构成的的连通块是一致的。因为我们考虑在未被染色的这个连通块内进行换根,其他子树必然没有发生结构变化所以染色情况不变,因此穿过这个连通块的所有路径均不能选,在这个情况下染色数组完全没有变化,因此这个连通块的任何一个节点都是 ok 的。

我们只需以每个点为根进行一次算法三的操作,然后对于每个连通块,对答案的贡献就是连通块大小的平方。

时间复杂度 $\Theta((n+m)n)$,预计得分 $60\%$。

算法五

我们如何快速计算一个节点是否 ok。注意到贪心选择出的每条路径的 LCA,在任何一组最大路径集合中,这些节点都必然各自被一条路径覆盖。因此我们考虑将贪心选择出的每个路径及其 LCA 作为基础,发现在 LCA 的子树中的,不经过其他 LCA 的这一连通块内部,不 ok 的节点显然是该 LCA 对应的路径的一个子段。为了注意到为了让一颗子树里的节点不被覆盖,我们可以通过更换路径使得新的不与其他路径冲突的路径,用于更新原本路径 ok 的部分。注意在此时可以牺牲上方的空间,因此这个思路必须是自顶向下更新的。

因此两类路径可以用于更换被选路径:

  1. 该路径的 LCA 是 ok 的,此时显然这一路径已经与某条路径相交,如果只与一条路径相交,则可以更新该路径。
  2. 该路径的 LCA 和某个 LCA 重合,且不与其它路径相交,此时可以更新该路径。

对于如何快速判断一条路径是否只与一条路径相交,与哪条路径相交,我们可以打一个标记分为 $0, v, -1$,表示这一节点向上被选择的 LCA 有 $0$ 个,有 $1$ 个并标记其编号,或者有 $\ge 2$ 个。这样只需通过 DFS 即可更新标记,且一个节点只会被更新 $2$ 次。

通过离线处理,我们可以做到时间复杂度 $\Theta(n + m\alpha(m,n))$,如果使用 $\Theta(n)-\Theta(1)$ LCA 技术也可以做到 $\Theta(n+m)$,预计得分 $100\%$。

BJ United Round #3 题解

2019-09-07 18:00:27 By EntropyIncreaser

三色树

这道题的主要目的是普及无标号无根树的计数方法,故不讨论非多项式时间做法。其实 $n$ 完全可以出得更大,但是没必要。

这道题改自 ProjectEuler#677

思路要点

首先考虑无标号有根树,我们只需维护 dp 数组 $R(n), B(n), Y(n)$ 即可。通过更新 dp 数组 $Q(d, n)$ 表示此时不在意节点的根的颜色,且根的度数为 $d$ 即可维护红根和蓝根,$W(d, n)$ 用于维护孩子没有黄根树的,这可以帮助计算黄根。每次 update 会消耗 $\Theta(n)$ 的时间,比如说已经计算完了 $R(n)$ 的值,那么它作为子树有可能出现了 $k$ 次,则选出 $\displaystyle \binom{R(n) + k - 1}{k - 1}$ 种方案,令 $Q(d,N)$ 加上 $\binom{R(n) + k - 1}{k - 1} Q(d - k, N - kn)$。

这一部分的复杂度为 $\Theta(n^2)$。

计算无标号无根树的要点是观察树的一个特殊点:一颗树要么存在唯一一个重心使得任何一个子树的大小均 $< \frac{n}2$,要么存在一个“重边”使得它将树恰好分为大小为 $\frac n2$ 的两部分。

对于前者部分的计数,我们只需将 dp 数组计算到 $\lceil \frac n2 \rceil - 1$ 时,将 $Q,R$ 数组取出。对于后者,若 $n$ 是偶数,当计算完 $\frac n2$ 的 dp 值时,将 $R, B, Y$ 用于计算即可。

如何做到更快

考虑计算 $R,B,Y$ 的生成函数 $G_R(x), G_B(x), G_Y(x)$。

我们记 $S = G_R + G_B + G_Y$,我们使用 Pólya 计数定理表示 $3$ 个孩子的树,通过枚举置换群: $$ \frac{S(x)^3 + 3S(x^2)S(x) + 2S(x^3)}{3!} $$

读者可自己尝试推导 $4$ 和 $2$ 个孩子的树。

由此我们可以通过 $G_R, G_B, G_Y$ 自身带入 $x, x^2, x^3$ 这些的多项式来表示。接下来考虑如何求解。

我们让 $G_R, G_B, G_Y$ 放在一起进行迭代,我们考虑倍增,由于已经计算出了前 $\frac n2$ 项,所有带入 $x^2, x^3$ 的部分就已经确定了,我们可以认为是常数。接下来就是一个关于 $G_R, G_B, G_Y$ 的三元三次方程,这依然可以通过牛顿迭代解决,也就是这等价于根据 $G_R, G_B, G_Y$ 的定义式得到了 $3$ 个它们之间的关系式 $E_R/E_B/E_Y(G_R, G_B, G_Y) = 0$,将 $E_R/E_B/E_Y(u, v, w)$ 在 $G_R, G_B, G_Y$ 处泰勒展开就是一次方程,求解一个三元一次方程即可。

本算法的复杂度为 $\Theta(n\log n)$,常数显而易见的大,欢迎有兴趣的同学来实现

押韵

事实上本题是 UOJ #450. 【集训队作业2018】复读机 的一个加强版。

算法一

$n\le 5\times 10^4$,我可以做多项式快速幂!这个多项式就是 $(\sum_{j\ge 0} \frac{x^{jd}}{(jd)!})^k$。这个模数不太友好,你可能需要 MTT……常数应该很大。

时间复杂度 $\Theta(n\log n) \sim \Theta(n\log n \log k)$,前者是进行多项式 $\ln , \exp$ 达到的复杂度,但是常数可能很大。

预计得分 $20\%$。

算法二

我们考察 $(\sum_{j\ge 0} \frac{x^{jd}}{(jd)!})^k$ 可以如何表示。

事实上与周期函数有关的,我们可以自然想到类似 DFT 的形式,也就是考虑到 $\sum_{j=0}^{d - 1} \omega_d^{cj} = d[d | c]$,因此我们可以知道上面的那个多项式实际上是

$$ \frac1d \sum_{j=0}^{d-1} \exp \omega_d^j x $$

我们直接枚举拆括号的过程中计算了几个 $\exp \omega_d^0 x$,几个 $\exp \omega_d^1 x$……然后这一部分的贡献就是 $(c_0 + c_1 \omega_d^1 + c_2 \omega_d^2 + \dots)^n$。最后总和除以 $d^k$。

时间复杂度 $\Theta(k^{d-1}\log n)$,预计得分 $50\%$。

算法三

主要思想还是围绕优化计算下式:

$$ [x^n]\left( \frac1d \sum_{j=0}^{d-1} \exp \omega_d^j x \right)^k $$

对于 $d=4$ 的情况,我们实际上是只需要统计每个 $\exp (a + b\mathrm{i})x$ 出现多少次。通过推导一些式子,或者直接将复平面旋转 $45^\circ$ 我们可以得到,方案数是 $\displaystyle\binom{k}{\frac{k + |a+b|}2} \binom{k}{\frac{k + |a-b|}2}$。因此答案可以在 $\Theta(k^2 \log n)$ 内计算出来。

预计得分 $70\%$。

算法四

对于 $d=6$ 的情况,我们注意到 $\omega_6$ 的代数关系有 $\omega - 1 = \omega^2$,因此所有的根都可以用 $1, \omega$ 表示,即给每个数赋予了一个离散的二维坐标 $$ \begin{array}{clclc} \omega^0 & = & 1 & & \\ \omega^1 & = & & & \omega\\ \omega^2 & = & -1 & + & \omega\\ \omega^3 & = & -1 & & \\ \omega^4 & = & & &-\omega\\ \omega^5 & = & 1 & + &-\omega \end{array} $$ 因此我们只需要做一个二维的快速幂,倍增 FFT 可以做到 $\Theta(k^2 \log k)$。但是常数可能较大。

预计得分 $80\% \sim 100\%$。

算法五

考虑计算一个二元母函数的高阶幂,$G(x, y) = F(x, y)^k$,可以得到 $F \frac{\partial G}{\partial_x} = kG \frac{\partial F}{\partial_x}$,由此可列得递推式子解出所有系数。计算高阶幂的复杂度是 $\Theta(k^2)$ 的。复杂度 $\Theta(k^2 \log n)$ 且常数较小。

事实上这也是可以直接用来做 $k=4$ 的情况的。

预计得分 $100\%$。

further

对于更大的 $d$,我们期望能得到一个怎样的做法?考虑 $d$ 次单位根的代数关系,我们希望能够基于一个它们的有理系数线性组合的基。由此则能将维度缩到基的维度,在其上进行快速幂。

我将说明维度可以达到 $\varphi(d)$,而数学迷告诉我说通过一些关于域的理论可以证明这也是下界,等我学了之后补一下证明

考虑分圆多项式 $\Phi_d(x) = \prod_{0\le k < d, \gcd(d, k) = 1} (x - \omega_d^k)$,它的次数显然是 $\varphi(d)$。

显然分圆多项式也可以用容斥原理重写,即

$$ \Phi_d(x) = \prod_{k|d} (x^{d/k}-1)^{\mu(k)} $$

显然该多项式的系数都是整数。由于 $\Phi_d(\omega_d) = 0$,不难得到 $\omega_d^k = \left.x^k \bmod \Phi_d\right \vert_{x=\omega}$。因此这个多项式的取模自然而然地导出了每个单位根到 $1, \omega_d, \dots, \omega_d^{\varphi(d) - 1}$ 的线性组合。

观察星象

算法一

显然所选的圆上要么有两个点要么有至少三个。因此分别枚举,枚举两个点作为圆的直径,此外枚举三个点之后可以确定唯一一个外接圆。

时间复杂度 $\Theta(n^4)$,预计得分 $10\%$。

算法二

$m=n$ 的情况就是经典的最小圆覆盖问题。

存在期望时间复杂度 $\Theta(n)$ 的算法,预计得分 $20\%$。

算法三

我们考虑二分答案。那么接下来枚举哪个点在圆上,其它每个点我们可以计算出它对于当前圆在哪个夹角区间内,进行一遍前缀和即可查找出是否存在一个夹角使得该圆包含 $m$ 个点。

时间复杂度 $\Theta(n^2\log n \log \frac{x}{\epsilon})$,预计得分 $60\%$。

算法四

我们对算法三进行进一步的观察。事实上我们可以看成这样一个问题,对于第 $i$ 个点,客观上存在一个 $r_i$ 即最小的圆的半径使得圆内有 $m$ 个点(我们认为无解即 $+\infty$),我们在二分的过程中,总共会预计调用 $\Theta(n\log \frac{x}{\epsilon})$ 次检查。这实际上是有所浪费的,在同样的检查次数内,实际上我们还可以算出每个 $i$ 对应的 $r_i$,换句话说我们多获取了不必要的信息,因为我们只关心最小值。

接下来就是有趣的地方了:我们先将序列随机打乱,并把当前最小值设为 $a = +\infty$。接下来我们将点一个个考虑,我们实际上可以一次检查出当前点的答案是否可以 $< a$。如果并不小于,我们就可以略过它,否则对该点的答案进行二分。这样显然是进行 $n + L\log \frac{x}{\epsilon}$ 次检查,其中 $L$ 是 $r_i$ 序列的单调栈长度。

显然最坏情况是 $r$ 互不相同时,单调栈长度的期望最长,这等价于一个排列的期望长度。而这一期望长度等于 $H_n = \sum_{k=1}^n \frac 1k = \Theta(\log n)$。

因此本算法的期望复杂度为 $\Theta( (n + \log n \log \frac{x}{\epsilon}) n\log n)$,预计得分 $100\%$。

BJ United Round #3

2019-09-04 10:30:06 By EntropyIncreaser
我们总是经不住考验所以需要被励志

也常常不明白命运带给我们磨难要表达的意思

向往安逸的人们总怀念着过去渴望能重返昔日

时间是一条永不回头的河流向着未来疾驰

                     ——幼稚园杀手《最初的我》

基本信息

出题人

  • T1:改编自一道 ProjectEuler
  • T2:某道题经 EntropyIncreaser 加强版本
  • T3:EntropyIncreaser

比赛难度

略低于 BJOI 2018 Day1。

BJ United Round #1 题解

2019-07-31 22:31:20 By EntropyIncreaser

极地科考

总负责人:EntropyIncreaser

简要题意:一个数列每次修改一个数,询问长度不低于 $k$ 的一段的最大平均值。

这是一道思维难度和代码难度都较低的题目,也与一些经典模型密切相关,故设计在第一题的位置。

算法一

枚举所有可行的区间,通过 $\frac ab - \frac xy = \frac{ay-bx}{by}$ 的正负来进行有理数比较。

时间复杂度:$\Theta(mn^2)$。

预计得分:$35$。

算法二

首先二分答案,假设答案为 $x$,那么将数列中每个数变为 $b_i = a_i - x$,我们只需知道是否存在一个长度至少为 $k$ 的段,其总和 $\ge 0$ 即可,维护一个前缀和的前缀最小值即可做到。最后,在二分的范围足够小时,则可以在 check 时记录找到的一个可行区间,来计算出其有理值答案。

时间复杂度:$\Theta(mn\log a)$。

预计得分:$60$。

算法三

注意到 $k=1$ 时直接选取序列中的最大值即为答案。

序列中的最大值有许多方式可以快速维护,例如线段树,堆。

时间复杂度:$\Theta(n + m\log n)$。

预计得分:$50$,综合前述得分 $75$。

算法四

考虑扩展算法三的观察,我们发现一定存在一个最优解的段长不超过 $2k - 1$,因为假设段长至少是 $2k$,那么我们将前 $k$ 个和后面的分别考虑,必有一段的平均值大于等于整段。

我们考虑分别对于长度 $L = k, k+1, \dots, 2k-1$,维护长度为 $L$ 的段中和最大的。当序列中一个值被修改时,只有最多 $L$ 个段包含它,因此只需修改堆中的 $L$ 个元素。

时间复杂度:$\Theta(nk + mk^2\log n)$。

预计得分:$85$。

算法五

考虑将修改一个元素看成将它加上一个值 $\Delta$,那么对于所有长度为 $L$ 的段,实际上是包含该位置的段内和全体加上这一值 $\Delta$,这些段对应于起点的一个区间。

我们每个 $L$ 用一个线段树来维护即可。

时间复杂度:$\Theta(nk + mk\log n)$。

预计得分:$100$。

梦中的项链

总负责人:EntropyIncreaser

这是一道中规中矩的计数题。这道题主要从容斥原理的角度考察了选手的组合计数能力,总的来说进行容斥的次数越多得分越多,最后考察了通过多项式技术进行优化计算。如果对于生成函数有一定了解则能在推导过程中事半功倍。

算法一

大力爆搜项链的重量序列?

时间复杂度:$\Theta(?)$。

预计得分:$0\sim 10$。

算法二

枚举第一个位置的重量 $w$,然后进行 DP。

设 $f(i, j)$:当前项链的总重量为 $i$,最后一个宝石的“抽象类型”为 $j$ 的方案数。抽象类型对于不为 $w$ 的每种宝石只有一种,对于 $w$ 重量的宝石来说有两种:是第一个位置的宝石种类或者不是。总共有 $\Theta(n)$ 种状态互相转移,因此一次 DP 的复杂度为 $\Theta(n^3)$。

时间复杂度:$\Theta(n^4)$。

预计得分:$20$。

算法三

注意到算法二中的状态之间互相转移可以通过记录全体的和进行优化,若重量转移 $j \neq k$,则方案数为 $a_k$,否则为 $a_k - 1$。这意味着我们实际上是先求和 $\sum_j f(i, j) \cdot a_k$,最后减去一个 $f(i, k)$。这样就可以减少一层枚举。

时间复杂度:$\Theta(n^3)$。

预计得分:$50$。

算法四

对于只有重量均为 $1$ 的情况,我们可以认为是给一个环进行上色,这是色多项式中的一个经典的模型,方案数应为 $(a_1 - 1)^n + (-1)^n(a_1 - 1)$。

时间复杂度:$\Theta(n)$。

预计得分:$10$,综合前述得分 $60$。

算法五

我们先不考虑第一个宝石和最后一个宝石的约束,那么剩下的约束就是一条简单的链,我们考虑先计算出此时的答案。

记宝石的生成函数为 $A(x) = \sum_{k \ge 1} a_k x^k$,我们考虑如何算出项链的生成函数。在此情况下,考虑 $F_n(x)$ 是有 $n$ 个宝石的链。我们假设最后一个宝石先随便放,再减去后面两个宝石相同的情况,再加上后面三个宝石相同的情况……可以得到式子 $$ F_n(x) = \sum_{k=1}^n F_{n-k}(x) A(x^k)(-1)^{k-1} $$ 我们考虑全体链的生成函数,也就是 $F(x)=\sum_{n\ge 0}F_n(x)$,将上式带入我们可以得到 $$ \begin{align} F(x)-1 &=F(x)\sum_{k\ge 1} (-1)^{k-1}A(x^k)\\ F(x) &= \frac{1}{1 - \sum_{k\ge 1} (-1)^{k-1} A(x^k)} \end{align} $$ 我们记 $I(x)=\sum_{k\ge 1} (-1)^{k-1}A(x^k)$。接下来考虑项链的约束。我们容斥的时候考虑最后一个宝石与序列的前 $p(p\ge 1)$ 个相同,与后 $q(q\ge 1)$ 个相同(包含自身),那么其贡献是 $A(x^{p+q})(-1)^{p+q-1}$。那么设 $p+q=j$,一共有 $j-1$ 组不同的 $p, q$,总共贡献是 $(j-1)A(x^j)(-1)^{j-1}$,我们记 $W(x)=1 + \sum_{j\ge 2}(-1)^{j-1}(j-1)A(x^j)$,看起来答案几乎就是 $F(x)W(x)$。

但是我们注意此时如果项链整体颜色相同的时候,贡献并不是 $0$,这是因为这个容斥假设不同的 $p, q$ 对应的方案是不同的,但是对于一个项链上的 $k$ 个宝石颜色全部相同,我们实际上在这个乘积中总共下来考虑了 $(-1)^{k-1}$ 次。因为我们的枚举方式实际上在某个位置插了一块板,而在所有宝石颜色都相同时候,这块板的位置不同就被计入多个方案了。因此我们要把这部分再减回去,也就是再减去 $I(x)$。

最后发现 $0$ 个宝石的方案恰被算了 $1$ 次,也要减去。故答案的母函数就是 $$ W(x)F(x)-I(x)-1 $$ 而运算过程中通共进行一次多项式求逆,外加一次多项式乘法。形如计算 $I(x)$ 的时候复杂度本身就是 $\Theta(n\log n)$。(事实上计算 $I(x)$ 可以做到 $\Theta(n\log \log n)$,但并非复杂度瓶颈,不予赘述)

算法复杂度:$\Theta(n\log n)$。

预计得分:$100$。

(之前的推导看起来混乱且理解起来十分费力。这里提供另一种思路。我们考虑对限制关系进行容斥,对于一个 $n$ 个宝石的项链,限制关系共有 $n$ 个,如果我们计入其中 $k$ 个限制,那么容斥系数就是 $(-1)^k$,从这个角度来看,就不需要在容斥的时候验证每个方案所被计算的贡献。)

求和

这道题来自 ROI2018 无进位加法。好像 cf 和 loj 上都没什么人刷,所以希望大家都没做过(

这是一道比较难的组合优化问题。其细节较多,得到平方级别的暴力还是比较轻松的,但是迈向正解还需要若干观察和分析。

算法一

考虑二分答案,即将答案从高到低决策该位是否可以为 $\texttt{0}$。这里二分的具体细节会导致复杂度不同,这里给出其中一个朴素二分中接近最高效的算法。

首先对答案进行估计:位数应当不超过 $n + \max L$。

其次我们考虑如果给出一个数,如果快速判断答案能否不大于它:我们从高到低分配给出的数中的每一个 $\texttt{1}$:我们将每个数当前最高位的 $\texttt{1}$ 放在一个数组里。

  • 如果当前所有数的最高位已经超过未分配数的最高位,则无解。
  • 否则如果相等,则我们只能将当前位分配给该数(如果有超过一个显然还是无解),可以看做该数直接将这一位去掉,如果有次高位,将次高位加入数组中。
  • 否则我们只要将这位分配给一个数,则该数直接清零,(考虑在一个可行解中交换分配的位)不难证明如果有解,我们可以用当前位将剩余的最大的数清零。
  • 考虑如何维护最大的数,我们预先将每个 $\texttt{1}$ 和它同位置有 $\texttt{1}$ 的数在一起排序,这个可以通过按顺序的基数排序来解决。然后我们每次取最大的数。
  • 看起来上述方法需要排序,实则不然,我们只需考虑在这位的数中是否会有一个没有被清零,因此我们并不需要执行排序,而只需要把最小的数放在最后处理。

由于每次考虑一位最多使得数组里插入一个元素,所以数组里的总元素是 $\Theta(n + \max L)$ 而非 $\Theta(\sum L)$。

时间复杂度:$\Theta(\sum L + (n + \max L)^2)$。

预计得分:$56$。

算法二

考虑所有数都只有一个 $\texttt{1}$ 的情况。

假设这些数从大到小排的最高位分别是 $L_1, L_2,\dots L_n$,那么答案的最高位可以发现是 $B = \max \{L_i + i - 1\}$。

这显然是一个下界,因为第一个达到这个数的 $i$,只考虑 $1\sim i$ 的这些数就必须至少占领这位。

而这显然有解,因为考虑将最大的数调至该位置,剩下的数对应的 $B$ 值至少 $-1$,归纳即得。

时间复杂度:$\Theta(n\log n + \sum L)$。

预计得分:$12$,综合前述得分 $68$。

算法三

上述观察事实上对于正解有着极大的意义。我们考虑将所有数都只保留最高位,得到的数列记为 $a'_i$,那么原数列的最高位应当在 $B$ 和 $B+1$ 之间。只需注意一点:将数列 $a'_i$ 每个数都左移 $1$ 位,那么得到的数均大于对应位置的 $a_i$,而这个数列的答案的最高位是 $B+1$!

我们考虑优化二分的过程。此时我们的决策要做的就更加明确了:确定 $B+1$ 这一位是否可以是 $\texttt{0}$。

考虑还有一些不用二分的位置:假设 $B$ 取到的第一个位置是 $k$,也就是说对于 $i < k$,都有 $L_i + i - 1 < B$,那么无论我们是从 $B+1$ 开始赋值,还是从 $B$ 开始赋值,这些数都一定不用在后面的计算中被考虑了,从整体上看,从 $B$ 位到 $B - k + 2$ 位的值都已经必然是 $\texttt{1}$ 了,而二分的结果其实额外决定的是对于前 $k$ 个数,还有一个 $\texttt{1}$ 是填在 $B+1$ 位置还是 $B-k+1$ 位置。

我们考虑先假设 $B$ 位足够,那么第 $k$ 个数就需要减去最高位后考虑剩下位的贡献,它会被重新插入进序列进行排序,我们可以通过算法一预先进行的基数排序,算出每个 $\texttt{1}$ 位置作为数的后缀,整体之间的序,然后用一个线段树来快速维护。

我们假设当前的二分如果返回可行,则已经计算出剩余部分的最优解。这样一来,二分的额外复杂度就是所有的返回“失败”的情况的复杂度之和。而在我们进行二分的时候,剩余的位数已经卡到了某个 $L_k$,如果二分失败,那么这一部分的复杂度就是 $\Theta(L \log \left( \sum L\right))$,注意到引发一次失败后,这个数就在接下来的过程中直接消失,因此不会被再次贡献复杂度。因此

时间复杂度:$\Theta\left(\left(\sum L\right) \log \left(\sum L\right)\right)$。

预计得分:$100$。

EntropyIncreaser Avatar