REOI Round #1 slution
T1 春日窃贼
要求构造 个区间使得与给定区间交集长度为 ,容易想到能够达到的 的上界为 ,那么考虑先对齐给定的 数组,再去调整区间达到减小交集长度的目的。
令答案数组为 ,第 段的交集长度为 ,一个很直觉的想法是把 的每一个点尽量向前移,直到移不动或者交集长度恰好为 就停止。
因为存在 , 的限制,所以这样做会导致 无法减小,也就是说这样构造的交集下界即为 。
可交集是可以更小的,进一步地,我们发现无论怎样移动,始终会存在 使得 ,即某个答案区间完全包含原区间,称这个区间为中心区间。
考虑枚举中心区间,并计算这种情况下的答案下界。
显然对于第 个区间作为中心区间时, 数组形如这样时交集最小:
把 全都放在序列头部, 全都放在序列尾部,若此时交集为 ,且 ,那么该中心区间就是一个合法的中心区间。
考虑如何计算 ,因为相邻两个区间作为中心区间的时候,对应的 数组只会有一个点移动,代价是容易计算的。
找出任意一个满足条件的中心区间 ,将点 向前移,点 向后移,移的过程中如果交集长度恰好为 就终止即可。
如果没有满足条件的中心区间说明无解。
原题是没有 的,要求输出字典序最小解,结合贪心思想,构造解时选择最靠后的一个中心区间即可,总时间复杂度 。
T2 靛蓝二次方
令最后满足条件的点为关键点,原矩阵为 , 不随题解中描述的操作而变化,始终是最初状态。
先考虑一个初始态为 的点想要成为关键点的条件,令其坐标为 ,有两种操作方式:
- 对于所有满足 的 ,翻转第 行;对于所有满足 的 ,翻转第 列。
- 翻转第 行和第 列,然后对于所有满足 的 ,翻转第 行;对于所有满足 的 ,翻转第 列。
同理,可以得到初始态为 的点想要成为关键点的操作方式:
- 翻转第 行,然后对于所有满足 的 ,翻转第 行;对于所有满足 且 的 ,翻转第 列。
- 翻转第 列,然后对于所有满足 且 的 ,翻转第 行;对于所有满足 的 ,翻转第 列。
除了上述提到的操作,其余操作均不能进行,否则 就无法成为关键点。
发现这个限制很强,于是可以考虑钦定一个点为最终的关键点,对于两种操作方式分别模拟,再对操作后的矩阵统计关键点个数。
这样做时间复杂度为 ,可以获得 分。
观察到在统计过程中用到的信息均为 串,且每一行最多一个关键点,加入 可以将单次统计复杂度降至 。
这样我们就得到了一个复杂度为 的算法,期望得分 分,但常数较大。
上述做法中,我们在知道一个点为关键点的条件之后,还去模拟操作过程是非常笨的。
考虑同时成为关键点的两点之间的关系。令点 成为关键点时的两种方式对应的操作集合分别为 和 ,显然 ,那么若点 和点 能够共存,当且仅当 或 或 或 ,即操作要完全相同,多个点同理。
有效的操作集合最多只有 个,容易想到把所有操作集合进行哈希,然后统计每个操作集合对应点的个数,取最大值即可。
哈希后可以直接用 或 ,但是建议使用常数较小的排序。
令 ,单组复杂度为 ,注意到 ,所以可以通过。
T3 仅予你的晴天
子任务 4
因为只会在序列尾部进行操作,所以显然可以对序列从左到右建可持久化权值线段树,每次操作要么加入一个数,要么回退一些,答案即 的答案减去 的答案。
子任务 5
因为只有加点操作,所以最终的序列是固定的,离线过后就变成子任务 3,这里不过多讲述。
从另一个角度考虑,如果我们从一个点把序列分成两部分,那么只需要把询问区间对应的至多两部分的答案加起来就是答案了。
显然,在这个子任务中,如果一个点能把原序列分成两部分,那么之后的每一个序列都可以被这个点分成两部分,然后对这两部分分别用子任务 4 的做法求解即可。
子任务 6
考虑为什么子任务 5 的做法不能放到这里来。
显然,如果询问时的序列不包含原始序列上选择的点,那么无论选哪个点,都不可能直接得到正确的答案。
此时,不难想到把原始序列更新为新序列,再选一个点,重新建树。
如果随便选点,显然很复杂度无法保证,那尝试找到一个特殊点,使得其复杂度能够保证。
注意到如果每次重构都选择新序列的中点,平均下来每个点至多只会被加入树中三次:
- 当该点被加入序列时,会被加入一次。
- 第一次被重构时,会被加入一次。
- 之后,每次将该点加入树中,它上一次被加入时,它关于上一次选择的点对称的点必定被删除。
时间复杂度 。
T4 所以我放弃了音乐
使用爆搜可以获得 分。
注意到 号点的特殊性质可以直接用数据结构做,这样就能获得 分的好成绩。
以上是大众分,与正解无关。
我们枚举两个位置,考虑这两个位置最后会变成什么。
记这两个位置为 ,初始时上面的数分别为 。
考虑按轮数 。我们尝试去枚举每次选定交换的两个位置,然后转移到下一层。
我们注意到除了 这两个位置,其他的位置在我们考虑这两个数时本质相同,因为题目的操作有可能选到任意一个位置。
也就是说,每个不是 的数最后被换到了 或是 上的方案总数是相同的。
所以我们可以把所有不是 的数看成一个数考虑,这里记作 。
则在若干次操作后,这两个位置可能出现的情况如下:
- (记作 ,下同) ->
dp[x][0]
- ->
dp[x][1]
- ->
dp[x][2]
- ->
dp[x][3]
- ->
dp[x][4]
- ->
dp[x][5]
- ->
dp[x][6]
系数可以自己推一下,比较麻烦。这样的复杂度是 的,常数为 。
然后我们来考虑每一个数对 ,并计算它对答案的贡献。
对于 和 ,我们直接判断 数对是否产生不和谐度即可。
对于 ,我们发现最后结果的 有可能是除了 之外剩下所有 个数之一。
结合我们刚刚讲到的所有其他的数本质相同这个结论,对于每个数,它作为 的方案数就是 dp[x][2 or 3 or 4 or 5] / (n - 2)
。
对于 ,因为所有其他的数本质都相同,所以去考虑任意一个其他数对,它们的本质也相同。
对于每个数对 ,它的方案数就是 dp[x][6] / ((n - 2) * (n - 3) / 2)
。
有多少种 会对不和谐度产生贡献,这是可以通过权值前缀和 求的。
总复杂度 。
注意到对于每个数对 ,它们最终计算出来的 dp
值是相同的。
所以 dp
做一遍就行了。复杂度 。
数据范围的提示很明显,这个转移是可以直接矩阵优化的。转移矩阵是一个 的,上面的系数就是你推出来的转移系数。
设最后推出来的系数为 。
然后对于每个数对 dp
值是相同的,这启示我们可以不用枚举每个,而是尝试用一些数据结构或者数学方法进行优化。
首先考虑 和 的情况。这就是二维偏序板子,一个树状数组求出来总的合法位置对数 ,则这一部分的贡献为 。
然后考虑 和 的情况。对于一个数对 ,可能的 有 种, 也有这么多种。
枚举 ,合并一下,前面一个可以用权值前缀和或者是树状数组算,后面一个也很好算。
和 的情况同理。
然后是 的情况。这一部分稍显麻烦。首先我们求出来总共有多少数对满足差值大于等于 ,记作 。这个可以用树状数组算。我们枚举左边那个位置 ,考虑每个右边位置 的贡献。容斥一下,用总的减去包含 的和包含 的,再看 这个数对是否被多减了。
为 种。
这个式子可以拆开来预处理一下,然后用前缀和去减。最后的 同样用树状数组做。
然后就做完了。总复杂度 。