2021.4.18 - 4.24 一周小结

这周做的题目,难度总体都挺高的,集中在 CF 上 3000 左右。并且在通过之前没有看任何一题的题解!(得瑟一下!)其实,有些难题,多想想,还是能艹过去的!Try a Try, AC is OK.(bushi)至于为什么看这些 CF 评级比较高的题呢?因为感觉评分高一些的题,在题目风格上更加 OI 一些,也更是我所擅长的。

CF1408I. Bitwise Magic

题意:

给定 ,以及长度为 的序列 (保证元素互不相同)。

操作 次,每次随机选择一个 ,然后将其 。对于 输出最后序列的异或和为 的概率,答案对 取模。

题解:

洛谷题解区里的做法充斥着生成函数,的确看到这题想到的第一个想法就是二元生成函数:设 ,答案的 PGF 是 :于是问题就可以转化成快速求多个二元多项式的乘积。但是这么想是没有必要的,我有一个更加巧妙而简洁的做法:

设一种最终局面中,第 个数被操作了 次;那么对于一个可能的局面 ,它出现的可能性可以用可重排列计算:

于是,设 表示前 个数、操作了 次、影响为 的所有方案的当前阶乘倒数乘积的和,其中 。于是我们就有了一个 的做法。考虑如何加速。

我们注意到两个事情:1.复杂度实际上是 ;2.每个数等价,所以不按顺序统计不影响答案。

所以我们可以把东西序列分成若干段,在每个段内 的值域很小,段内可以快速 DP;最后再想办法拼起来。

由于 ,所以几乎只会影响到最后的 4 位;考虑到减法退位的情况,最多再将前面的某一位开始全部取反;所以我们可以按照它影响的是前面的哪一位分段,每一段内的 DP 数组中, 的取值个数都只有 个,可以快速地 DP,这一部分的总复杂度就是

然后考虑如何把这 个 DP 数组拼起来,第一维暴力卷积,第二维 fwt 即可,复杂度

评测链接:AC submission:https://codeforces.ml/contest/1408/submission/113971716.

UPD:事后 zc_li 强神告诉我说直接对第二维 fwt 一下就行了,我傻了。

CF1474F 1 2 3 4 ...

题意:

给定一个序列,求其中最长严格上升子序列长度及其个数。

序列按如下方式给出:给定 和序列中的第一个数 ,接下来 个数 ,若 ,则重复 次,在序列末尾加上当前序列最后一个数 的值,若 ,重复 次,在序列末尾加上当前序列最后一个数 的值。

题解:

假如这个像过山车一样的序列总长只有 该怎么做?考虑增量构造,设 表示当前序列中,结尾是 的最长上升子序列个数;那么增加一个数 也就等价于执行 ;依次加入数就行了,最后的答案只要统计那些 位置的值的和就行了(注意这个 是在 位置时的 );注意每次到达新的最低点时要将 数组清空。

现在考虑序列可能很长的情况。我们发现:下降操作,大致相当于把 这一段平移一下加到自己身上;上升操作,相当于把区间的第一个数加上一个数,然后把这个区间前缀和一下。

有没有合适的结构可以用来维护这个 DP 数组呢?有,那就是多项式!先给出一个结论:任一时刻的 数组皆可以被分成 段,每一段内的 均可以用次数小于 的多项式 表示。下面大致口胡一下:

  1. 序列 1,1,1,... 可以用 表示。
  2. 它的前缀和可以用 表示,它的前缀和的前缀和可以用 表示。...... 它的任意一阶前缀和都可以用多项式表示。
  3. 下降操作中,除了区间第一个数之外的 可以用 表示;第一个数可以单独拆成一个常数多项式。
  4. 上升操作中,前缀和操作可以用多项式表示;而第一个数加上一个数,相当于在这个多项式上加上一个常数;而这些使得这段的 可以看作是序列 1,1,1,... 的多阶前缀和的多项式的和。

于是我们可以大力用多项式维护这个 数组,每次操作直接算出这一段 的前 项,前缀和/偏移一下,然后把新的 直接拉格朗日插值插出来。

有一些细节,比如在下降操作中,第一个位置需要单独新建一个多项式;上升操作中,如果有一段的 已经超出了原来的范围,那么就需要新建一个常数多项式来表示这一段。

评测链接:AC submission:https://codeforces.ml/problemset/submission/1474/113669336.

CF1476F. Lanterns

题意:

个灯笼拍成一排,第 个灯笼具有 的亮度。每个灯笼要么朝向左,照亮左边编号为 的灯笼,要么朝向右,照亮右边编号为 的灯笼。

寻找一种方案,为所有的灯笼确定朝向,使得每一个灯笼被至少一个其他灯笼照亮。

题解:

Try a try,AC is OK!这里是

首先有一个 的 DP:设 表示使用了前 个灯笼、最左边需要点亮的灯笼是 、最右边已经照亮了灯笼 的一个状态,然后就可以快乐地 DP 了。

然后我们可以发现:同样的 ,只有最大的 是有用的,于是状态数和复杂度就可以减小到

但是还不能过,我们发现,对于同一个 ,合法的 应该是很少的——因为只有当前的灯笼的威力可以到达 我才会使它朝左,所以我们从左往右只保留这些合法状态来 DP,大致代码如下:

这样写,复杂度其实卡不满的,于是就过了。

评测链接:AC submission:https://codeforces.ml/problemset/submission/1476/113486661.

CF1430G. Yet Another DAG Problem

题意:

给定一个有向无环图,每条边都有 的权重,给每个点分配权值 ,对于每条连接 的边,定义其权值为 ,要求:

  1. 最小

请输出一种 的分配方案,

题解:

一开始可能会以为这个状态甚至可以按照拓扑序来贪心,但是最后一个样例就给出了反例——两条长短不同但是端点相同的链,这是不能直接贪心的。

那么我们考虑怎么 DP:一种朴素的状态设置是直接状压下已经填好的每一个点的权值,记为 ;设 表示当前填数填到了 、状压为 的最小花费。但是这样复杂度几乎等同于爆搜,行不通。

由于 ,所以我们考虑指状压下当前已经填好的数的集合 ,这样复杂度就是 的,但是怎么转移呢???

大开脑洞一下,我们费用提前计算,在 的转移中我们的代价,把其他还没选的状态都算上,也就是代价直接记为 集合所有出边的边权之和。这样做有点反直觉,因为代价竟然和 无关,甚至不用记录当前 DP 到了第几位;但是这样的确是对的。

复杂度最劣是 的,但是由于状压 DP 常常跑不满,我们相信它能过。

评测链接:AC submission:https://codeforces.ml/problemset/submission/1430/113711359.

CF1455G. Forbidden Value

题意:

已知初始值,给定下面2种命令:

  1. set ,令,或花费元钱删除该命令;
  2. if ... end,如果,执行if...end中的命令,否则跳过该if...end

你需要使用最少的花费,使得无论运行到哪里,都有。命令条数

题解:

给人的直接感觉就是类似树形 DP,考虑设 表示当前的值为 的最小开销;于是在添加一个操作,就等价于:

若是在最后添加一个 if,我们递归计算出这一个 if 内部的 ,合并就是:

用线段树维护即可,整体 DP 的 2900 的题也就直接秒掉了,也就是难写而已。

评测链接:AC submission:https://codeforces.ml/problemset/submission/1455/113822202.

CF1446D2. Frequency Problem (Hard Version)

题意:

给出个元素组成的序列,求最长的子段使得其中有至少两个出现次数最多的元素。

题解:

假设每个元素的出现次数都比较少,我们可以枚举这个出现次数的上限 ,对于每一个 ,我们都对原数列从右到左进行一次双指针,每一次操作左移一次右端点,然后尽可能地在满足 的限制条件的情况下左移左端点。依次判断每个满足 限制的极大区间是否最少两个众数。

这样做为什么是对的?1. 这个最后选出的区间的 一定会被枚举到;2. 对于同一个右端点,在同样 的限制下,越往左移左端点越能满足至少有两个众数的条件。

假设元素的取值范围很小怎么做呢?我们发现原数列的众数一定是这个子段的众数之一,证明使用反证法,设它不是,那么左移左端点,然后右移右端点,大众数和区间众数的出现次数会是两个相交的分段函数,也就意味着我总可以通过扩大区间使得众数包括大众数,并同时最优化答案;所以不为大众数的情况不存在。

我们枚举另一个众数是哪一个,我们只要找到这个数和大众数出现次数相同的极大区间即可,因为答案中的众数一定包括大众数,于是一旦与大众数相同,便一定是这个答案区间的众数;加入不是,那么它一定不是答案区间,并且存在长度更长的答案区间(证明同上)。

这时惊世骇俗的一步来了!我们分出现次数小于 和出现次数大于 分治一下,分别使用上述两种方法,就能实现

这一题我想到了小于 , Zenislt 大佬想到了大于 ,合作一下就过了。

评测链接:AC submission:https://codeforces.ml/problemset/submission/1446/113452417.

CF1487G. String Counting

题解:

你有 个不同的字符,第 个字符有 个()。

你希望用这些字符,构造出一个字符串(每个字符在字符串中出现的个数不超过 ),使得这个字符串上不存在长度为奇数且大于 的回文串。求出方案数对 取模的结果。

题解:

这题是在补之前的题,所以有参考题解。

意味着什么呢?意味着最多有两个字母非法,我们不妨设它们是 ab,于是设 表示有前 个字母有 ab、前两位是不是 ab(0/1/2)、前一位是不是 ab(0/1/2)。

于是就可以快乐 地 DP,设 ,于是答案可以表示为:

评测链接:AC submission:https://codeforces.ml/problemset/submission/1487/113459203.

CF963D. D. Frequency of String

题意:

给你一个字符串 ,有 个询问,第 个询问包含一个整数 和一个字符串 。要求找到一个字符串 ,使得 的子串并且 至少在 中出现了 次。你只需要求出 的最短长度。

保证 互不相同。

题解:

把询问串建出 ACAM,然后遍历一遍 ,找到每一个询问串的 Endpos 的集合,判断一下;由于 FJOI 考场上的那个 Trick,这么直接暴力做是 的。

主要是十分感慨,感觉 FJOI 考场上写的也几乎是这份代码,只是把 ACAM 换成了 广义 SAM,我在想,倘若我没有看错题,那么考场上写的也许有就是这份代码了吧!那也许解决就与现在不同了吧!

写着写着,我感慨万分,但并不后悔或是遗憾;现在的我要做的,是拼搏拼搏再拼搏!

He Will come back soon.