这周做的题目,难度总体都挺高的,集中在 CF 上 3000 左右。并且在通过之前没有看任何一题的题解!(得瑟一下!)其实,有些难题,多想想,还是能艹过去的!Try a Try, AC is OK.(bushi)至于为什么看这些 CF 评级比较高的题呢?因为感觉评分高一些的题,在题目风格上更加 OI 一些,也更是我所擅长的。
给定 ,以及长度为 的序列 (保证元素互不相同)。
操作 次,每次随机选择一个 ,然后将其 。对于 输出最后序列的异或和为 的概率,答案对 取模。
洛谷题解区里的做法充斥着生成函数,的确看到这题想到的第一个想法就是二元生成函数:设 ,答案的 PGF 是 :于是问题就可以转化成快速求多个二元多项式的乘积。但是这么想是没有必要的,我有一个更加巧妙而简洁的做法:
设一种最终局面中,第 个数被操作了 次;那么对于一个可能的局面 ,它出现的可能性可以用可重排列计算:
于是,设 表示前 个数、操作了 次、影响为 的所有方案的当前阶乘倒数乘积的和,其中 。于是我们就有了一个 的做法。考虑如何加速。
我们注意到两个事情:1.复杂度实际上是 ;2.每个数等价,所以不按顺序统计不影响答案。
所以我们可以把东西序列分成若干段,在每个段内 的值域很小,段内可以快速 DP;最后再想办法拼起来。
由于 ,所以几乎只会影响到最后的 4 位;考虑到减法退位的情况,最多再将前面的某一位开始全部取反;所以我们可以按照它影响的是前面的哪一位分段,每一段内的 DP 数组中, 的取值个数都只有 个,可以快速地 DP,这一部分的总复杂度就是 。
然后考虑如何把这 个 DP 数组拼起来,第一维暴力卷积,第二维 fwt 即可,复杂度 。
评测链接:AC submission:https://codeforces.ml/contest/1408/submission/113971716.
UPD:事后 zc_li 强神告诉我说直接对第二维 fwt 一下就行了,我傻了。
给定一个序列,求其中最长严格上升子序列长度及其个数。
序列按如下方式给出:给定 和序列中的第一个数 ,接下来 个数 ,若 ,则重复 次,在序列末尾加上当前序列最后一个数 的值,若 ,重复 次,在序列末尾加上当前序列最后一个数 的值。
假如这个像过山车一样的序列总长只有 该怎么做?考虑增量构造,设 表示当前序列中,结尾是 的最长上升子序列个数;那么增加一个数 也就等价于执行 ;依次加入数就行了,最后的答案只要统计那些 的 位置的值的和就行了(注意这个 是在 位置时的 );注意每次到达新的最低点时要将 数组清空。
现在考虑序列可能很长的情况。我们发现:下降操作,大致相当于把 这一段平移一下加到自己身上;上升操作,相当于把区间的第一个数加上一个数,然后把这个区间前缀和一下。
有没有合适的结构可以用来维护这个 DP 数组呢?有,那就是多项式!先给出一个结论:任一时刻的 数组皆可以被分成 段,每一段内的 均可以用次数小于 的多项式 表示。下面大致口胡一下:
1,1,1,...
可以用 表示。1,1,1,...
的多阶前缀和的多项式的和。于是我们可以大力用多项式维护这个 数组,每次操作直接算出这一段 的前 项,前缀和/偏移一下,然后把新的 直接拉格朗日插值插出来。
有一些细节,比如在下降操作中,第一个位置需要单独新建一个多项式;上升操作中,如果有一段的 已经超出了原来的范围,那么就需要新建一个常数多项式来表示这一段。
评测链接:AC submission:https://codeforces.ml/problemset/submission/1474/113669336.
有 个灯笼拍成一排,第 个灯笼具有 的亮度。每个灯笼要么朝向左,照亮左边编号为 的灯笼,要么朝向右,照亮右边编号为 的灯笼。
寻找一种方案,为所有的灯笼确定朝向,使得每一个灯笼被至少一个其他灯笼照亮。。
Try a try,AC is OK!这里是 过 。
首先有一个 的 DP:设 表示使用了前 个灯笼、最左边需要点亮的灯笼是 、最右边已经照亮了灯笼 的一个状态,然后就可以快乐地 DP 了。
然后我们可以发现:同样的 ,只有最大的 是有用的,于是状态数和复杂度就可以减小到 。
但是还不能过,我们发现,对于同一个 ,合法的 应该是很少的——因为只有当前的灯笼的威力可以到达 我才会使它朝左,所以我们从左往右只保留这些合法状态来 DP,大致代码如下:
xxxxxxxxxx
131for (int i = 1; i <= n; i++)
2{
3sort(dp[i - 1].begin(), dp[i - 1].end(), [](node x, node y) { return x.l == y.l ? x.r > y.r : x.l > y.l; });
4int mx = -1e9;
5for (int j = 0; j < dp[i - 1].size(); j++)
6{
7node u = dp[i - 1][j];
8if (u.r <= mx) continue;
9if (u.l >= i - p[i] && u.l != i) dp[i].push_back({ u.r < i ? i : n + 1, u.r, j, 0 });
10dp[i].push_back({ min(u.l, u.r < i ? i : n + 1), max(u.r, i + p[i]), j, 1 });
11mx = max(mx, u.r);
12}
13}
这样写,复杂度其实卡不满的,于是就过了。
评测链接:AC submission:https://codeforces.ml/problemset/submission/1476/113486661.
给定一个有向无环图,每条边都有 的权重,给每个点分配权值 ,对于每条连接 的边,定义其权值为 ,要求:
- 最小
请输出一种 的分配方案,。
一开始可能会以为这个状态甚至可以按照拓扑序来贪心,但是最后一个样例就给出了反例——两条长短不同但是端点相同的链,这是不能直接贪心的。
那么我们考虑怎么 DP:一种朴素的状态设置是直接状压下已经填好的每一个点的权值,记为 ;设 表示当前填数填到了 、状压为 的最小花费。但是这样复杂度几乎等同于爆搜,行不通。
由于 ,所以我们考虑指状压下当前已经填好的数的集合 ,这样复杂度就是 的,但是怎么转移呢???
大开脑洞一下,我们费用提前计算,在 的转移中我们的代价,把其他还没选的状态都算上,也就是代价直接记为 集合所有出边的边权之和。这样做有点反直觉,因为代价竟然和 无关,甚至不用记录当前 DP 到了第几位;但是这样的确是对的。
复杂度最劣是 的,但是由于状压 DP 常常跑不满,我们相信它能过。
评测链接:AC submission:https://codeforces.ml/problemset/submission/1430/113711359.
已知初始值,给定下面2种命令:
set
,令,或花费元钱删除该命令;if
...end
,如果,执行if...end
中的命令,否则跳过该if...end
。你需要使用最少的花费,使得无论运行到哪里,都有。命令条数 。
给人的直接感觉就是类似树形 DP,考虑设 表示当前的值为 的最小开销;于是在添加一个操作,就等价于:
若是在最后添加一个 if
,我们递归计算出这一个 if
内部的 ,合并就是:
用线段树维护即可,整体 DP 的 2900 的题也就直接秒掉了,也就是难写而已。
评测链接:AC submission:https://codeforces.ml/problemset/submission/1455/113822202.
给出个元素组成的序列,求最长的子段使得其中有至少两个出现次数最多的元素。
假设每个元素的出现次数都比较少,我们可以枚举这个出现次数的上限 ,对于每一个 ,我们都对原数列从右到左进行一次双指针,每一次操作左移一次右端点,然后尽可能地在满足 的限制条件的情况下左移左端点。依次判断每个满足 限制的极大区间是否最少两个众数。
这样做为什么是对的?1. 这个最后选出的区间的 一定会被枚举到;2. 对于同一个右端点,在同样 的限制下,越往左移左端点越能满足至少有两个众数的条件。
假设元素的取值范围很小怎么做呢?我们发现原数列的众数一定是这个子段的众数之一,证明使用反证法,设它不是,那么左移左端点,然后右移右端点,大众数和区间众数的出现次数会是两个相交的分段函数,也就意味着我总可以通过扩大区间使得众数包括大众数,并同时最优化答案;所以不为大众数的情况不存在。
我们枚举另一个众数是哪一个,我们只要找到这个数和大众数出现次数相同的极大区间即可,因为答案中的众数一定包括大众数,于是一旦与大众数相同,便一定是这个答案区间的众数;加入不是,那么它一定不是答案区间,并且存在长度更长的答案区间(证明同上)。
这时惊世骇俗的一步来了!我们分出现次数小于 和出现次数大于 分治一下,分别使用上述两种方法,就能实现 。
这一题我想到了小于 , Zenislt
大佬想到了大于 ,合作一下就过了。
评测链接:AC submission:https://codeforces.ml/problemset/submission/1446/113452417.
你有 个不同的字符,第 个字符有 个()。
你希望用这些字符,构造出一个字符串(每个字符在字符串中出现的个数不超过 ),使得这个字符串上不存在长度为奇数且大于 的回文串。求出方案数对 取模的结果。
这题是在补之前的题,所以有参考题解。
意味着什么呢?意味着最多有两个字母非法,我们不妨设它们是 a
和 b
,于是设 表示有前 个字母有 个 a
和 个 b
、前两位是不是 a
或 b
(0/1/2)、前一位是不是 a
或 b
(0/1/2)。
于是就可以快乐 地 DP,设 ,,于是答案可以表示为:
评测链接:AC submission:https://codeforces.ml/problemset/submission/1487/113459203.
给你一个字符串 ,有 个询问,第 个询问包含一个整数 和一个字符串 。要求找到一个字符串 ,使得 是 的子串并且 至少在 中出现了 次。你只需要求出 的最短长度。
保证 互不相同。
把询问串建出 ACAM,然后遍历一遍 ,找到每一个询问串的 Endpos 的集合,判断一下;由于 FJOI 考场上的那个 Trick,这么直接暴力做是 的。
主要是十分感慨,感觉 FJOI 考场上写的也几乎是这份代码,只是把 ACAM 换成了 广义 SAM,我在想,倘若我没有看错题,那么考场上写的也许有就是这份代码了吧!那也许解决就与现在不同了吧!
写着写着,我感慨万分,但并不后悔或是遗憾;现在的我要做的,是拼搏拼搏再拼搏!
He Will come back soon.