A. Marketing Scheme
如果(2 cdot l le r)就可以,反之不行。
B. Reverse Binary Strings
易得:最后要么是010101...要么是101010...
分别枚举两种情况,算出每种情况下需要翻转的位置,记(r_i)为第(i)位是否需要翻转,(r)中极大全1子段的个数即为答案。
C. Chef Monocarp
看到(n)的取值范围就觉得不对劲,想了想直接(O(n^3))DP莽了。
首先猜结论:(t_i)小的盘子必定先操作。所以先给(t)排个序,方便之后DP。
记(dp_{i, j})表示考虑前(i)个盘子,第(i)个盘子在第(j)分钟操作。
然后转移方程是
最后的答案是
其中,400是时间的上界。
D. Minimal Height Tree
易得:如果(a_i < a_{i + 1}),那么(a_i)可以和(a_{i + 1})共享同一个父亲。反之,不行。
根据观察,就可以通过一个BFS的过程去构造出答案。
首先,在队列中加入((1, 0))。二元组第一个元素表示节点上的值,第二个元素表示节点所在深度。
然后从(2)到(n)依次处理元素:
- 每次拿出队首,如果队首没有子节点或者说子节点的最大值小于当前元素,那么当前元素就可以作为队首的子节点,深度位队首的深度加一。所以将((a_j, depth + 1))入队。
- 否则,说明队首无法再接受任何子节点,就将队首出队,然后再走到第一步。
这样通过BFS保证了构造出来的树的BFS序满足条件,并且尽可能地利用了已有的节点,有点贪心的意思吧。
E. Make It Increasing
这题其实挺显而易见的吧,一眼看出了做法,就是根据(b)中的元素将序列划分为多段,每段去做一个LIS,就是LIS要加限制条件而已。
然后调了一个多小时都没调出来。赛后看了jiangly大佬的代码,发现他让(a_i = a_i - i),代码编写难度直线下降。
具体讲一下吧,就是先让(a_i = a_i - i)。然后如果存在(a_{b_i} > a_{b_{i + 1}}),则无解。
然后我因为不想讨论边界,所以在两端假了哨兵节点。
现在就是根据(b)中的元素将(a)划分为多段,每一段可以单独求解出最小的代价,所有段最小代价的和就是总的最小代价。然后每一段的最小代价和就是(段长度-段带限制LIS长度),带限制就是LIS中的元素的值必须大于左端点,小于右端点。
jiangly的做法很大的减少了代码复杂度,因为如果不这样处理的话,在做带限制LIS时,为了判断是否满足限制还要判断下标的关系。而做了处理之后,如果大于就说明了满足限制,所以就容易写了很多。
G. Death DBMS
卡E,最后几分钟才看到这题,AC自动机板子,不过好久没写了大概率调不出来。
大概就是前(n)个人名算模式串,后面查询最大值的算文本串,然后就是多模式串匹配,然后就能想到AC自动机。存值就是每个节点开一个multiset,保存在当前节点结束的模式串的权值。
先将模式串都加入AC自动机,之后查询的时候就是把文本串拿到AC自动机里跑,失配的时候就暴力跳fail链,然后查询就是查询fail树上根到当前路径的最大值。然后询问路径最大值的时候跳fail链会TLE,所以想到了类似虚树的优化。就是在建fail树的时候要计算出每个节点上一个有效点的位置在那里,记为last。跳last链就不会TLE,复杂度好像可以证明是(O(n sqrt n))的。
更新点权就是插入的时候记录对应串截至在那里,然后根据节点id找到对应的multiset,然后修改就完事了。