7.9-7.11
7/10
总结:典型的手法我是一个都不会,并且一个紫都做不动。
项目经验
好题。
典型的 trick 是 exchange-argument(邻项交换),考虑将前后分成两段。段内,考虑邻项交换 \(i\) 和 \(j\),满足 \(a_i b_j \ge a_j b_i\),则 \(a_i / b_i \ge a_j / b_j\)。考虑插入的过程,发现其实可以算作每个前面的 \(i\) 都对后面的 \(j\) 产生了 \(a_i \sum b_j\) 的贡献,于是为了更方便,贡献提前计算。这要求我们每一段从前往后插入。
我们又发现了难题:同时做前后段是不容易算出前段的贡献的,因为后段的 \(\sum b_i\) 难算。这衍生出了一个想法,可以提前钦定后段的 \(\sum b_i\)。用 \(dp(i,j)\) 表示已经在前后插入了若干数,并且后段的 \(b_i\) 之和为 \(j\) 的最大总价值;这个状态设计不仅可以记录 \(targetB\) 的限制,也可以实时计算每一个数插入的贡献,是好的。最终答案为 \(dp(n/2,targetB)\),\(targetB\) 为枚举钦定的。然后就好做了。每个数插入后段的顺序和两段各自对答案的贡献都是好算的。复杂度 \(O(n^2/2\times B^2)\)。
这也启示我们:对这种难算的分段问题,可以先考虑段内顺序(本题用的是邻项交换)。然后再考虑用 dp 和钦定贡献计算段间分配。
7/11
总结:人菜瘾大。
CF992E Nastya and King-Shamans
T3 搬的这个题,我竟然没做出来/dk。记 \(s_i\) 为前缀和数组,King-Shamans 即需满足 \(s_i = 2 s_{i-1}\)。这个等于 \(0\) 看起来很难做啊,我们熟悉的是区间 \(\ge 0\) 的形式,因为这样可以用动态维护最大值。于是把条件放宽,\(s_i \ge 2 s_{i-1}\)。观察到:
如果看到形如有 \(x_i \ge k x_{i-1}\) 之类的东西,并且 \(x_n\) 固定,那么这个序列满足条件的 \(i\) 的数量为 \(O(\log_k n)\) 级别。
注意到这个事情就好做了。我们直接维护每个位置的 \(a_i - s_{i-1}\),线段树维护区间 \(max\),然后每次修改后直接暴力查询就好了。复杂度是 \(O(n\log n \log (\sum a))\) 级别的。
CF11E Forward, march!
T4 搬的是这个题,我竟然没做出来/dk。
首先我们发现序列最终长度为偶数是好做的,因为每个位置是否合法只跟奇偶性有关。然后考虑奇数,它显然是不优的,因为你奇数相当于将字符串蠕动了一个位置,所以百分比相当于原来的 \(L\) 和 \(R\) 个数之和 \(/Len / 2\)。然而你如果把它补成一个偶数,你只需要在前后两种方案种取最大的选即可,并且是不劣的。所以只需要考虑偶数的情形。
求分数最优的显然用分数规划。考虑二分出来了 \(mid\),推一下不等式,发现每个可行的位置对答案产生 \(1-mid\) 的贡献,不可行的位置产生 \(-mid\) 的贡献。我们发现这个总共有多少位置是对答案不影响的,于是可以直接对着原字符串做 dp。
我们先把原来的字符串插 X 变得合法。中间的间隙是好插入的。但是如果左右两边相同,那么在循环中是不符合规范的。应当在开头或者结尾强制插入一个 X。于是可以分类讨论,用下文 \(f\) 的状态来表示,发现若是 L 则在结尾,R 则在开头。
然后注意一件事情:
- 一个间隙最多插入 \(1\) 个
X,要么不插。
然后可以 dp 了。\(f(i,0/1)\) 表示原序列到第 \(i\) 位,末尾现在对应着偶数R/奇数L位置时刻的最大价值。dp 是好推的。注意边界条件,\(f(0,0)=0\),开头(或者末尾,是等价的)插入一个 \(f(0,1)=-mid\)。由于偶数满足最优性,那么最终直接判一手 \(f(n,0) \ge 0\) 即可。

浙公网安备 33010602011771号