BJ United Round #1 题解

极地科考

总负责人:EntropyIncreaser

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

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

算法一

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

时间复杂度: Θ ( m n 2 ) \Theta(mn^2)

预计得分: 35 35

算法二

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

时间复杂度: Θ ( m n log a ) \Theta(mn\log a)

预计得分: 60 60

算法三

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

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

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

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

算法四

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

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

时间复杂度: Θ ( n k + m k 2 log n ) \Theta(nk + mk^2\log n)

预计得分: 85 85

算法五

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

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

时间复杂度: Θ ( n k + m k log n ) \Theta(nk + mk\log n)

预计得分: 100 100

梦中的项链

总负责人:EntropyIncreaser

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

算法一

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

时间复杂度: Θ ( ? ) \Theta(?)

预计得分: 0 10 0\sim 10

算法二

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

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

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

预计得分: 20 20

算法三

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

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

预计得分: 50 50

算法四

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

时间复杂度: Θ ( n ) \Theta(n)

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

算法五

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

记宝石的生成函数为 A ( x ) = k 1 a k x k A(x) = \sum_{k \ge 1} a_k x^k ,我们考虑如何算出项链的生成函数。在此情况下,考虑 F n ( x ) F_n(x) 是有 n n 个宝石的链。我们假设最后一个宝石先随便放,再减去后面两个宝石相同的情况,再加上后面三个宝石相同的情况……可以得到式子

F n ( x ) = k = 1 n F n k ( x ) A ( x k ) ( 1 ) k 1 F_n(x) = \sum_{k=1}^n F_{n-k}(x) A(x^k)(-1)^{k-1}

我们考虑全体链的生成函数,也就是 F ( x ) = n 0 F n ( x ) F(x)=\sum_{n\ge 0}F_n(x) ,将上式带入我们可以得到

F ( x ) 1 = F ( x ) k 1 ( 1 ) k 1 A ( x k ) F(x)-1 =F(x)\sum_{k\ge 1} (-1)^{k-1}A(x^k)

F ( x ) = 1 1 k 1 ( 1 ) k 1 A ( x k ) F(x) = \frac{1}{1 - \sum_{k\ge 1} (-1)^{k-1} A(x^k)}

我们记 I ( x ) = k 1 ( 1 ) k 1 A ( x k ) I(x)=\sum_{k\ge 1} (-1)^{k-1}A(x^k) 。接下来考虑项链的约束。我们容斥的时候考虑最后一个宝石与序列的前 p ( p 1 ) p(p\ge 1) 个相同,与后 q ( q 1 ) q(q\ge 1) 个相同(包含自身),那么其贡献是 A ( x p + q ) ( 1 ) p + q 1 A(x^{p+q})(-1)^{p+q-1} 。那么设 p + q = j p+q=j ,一共有 j 1 j-1 组不同的 p , q p, q ,总共贡献是 ( j 1 ) A ( x j ) ( 1 ) j 1 (j-1)A(x^j)(-1)^{j-1} ,我们记 W ( x ) = 1 + j 2 ( 1 ) j 1 ( j 1 ) A ( x j ) W(x)=1 + \sum_{j\ge 2}(-1)^{j-1}(j-1)A(x^j) ,看起来答案几乎就是 F ( x ) W ( x ) F(x)W(x)

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

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

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

预计得分: 100 100

之前的推导看起来混乱且理解起来十分费力。这里提供另一种思路。

我们考虑对限制关系进行容斥,对于一个 n n 个宝石的项链,限制关系共有 n n 个,如果我们计入其中 k k 个限制,那么容斥系数就是 ( 1 ) k (-1)^k ,从这个角度来看,就不需要在容斥的时候验证每个方案所被计算的贡献。

求和

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

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

算法一

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

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

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

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

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

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

预计得分: 56 56

算法二

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

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

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

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

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

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

算法三

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

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

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

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

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

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

预计得分: 100 100

猜你喜欢

转载自blog.csdn.net/EI_Captain/article/details/100669778
bj