1 条题解

  • 0
    @ 2026-5-5 14:58:53

    E. Alex and Complicated Task 详细题解

    题目重述

    给定一个长度为 nn 的序列 aa。要找一个最长的子序列 bb(不要求连续),使得 bb 的长度为 4m4mmm 为整数),并且对于每个 kk(从 00 开始)满足:

    b4k+1=b4k+3,b4k+2=b4k+4.b_{4k+1} = b_{4k+3}, \quad b_{4k+2} = b_{4k+4}.

    换言之,bb 可以划分为若干个长度为 44 的块,每个块形如 x,y,x,yx, y, x, y(即两个数重复两次)。求最长可能的 bb 并输出任意一个。

    观察与转化

    条件 b4k+1=b4k+3b_{4k+1}=b_{4k+3}b4k+2=b4k+4b_{4k+2}=b_{4k+4} 等价于每个长度为 44 的块由两个数 xxyy 组成,形式为 x,y,x,yx,y,x,y。因此,我们需要在原序列 aa 中找到一个最长子序列,它能够被分割成若干这样的四元组,且这些四元组在子序列中按顺序出现。

    由于子序列保持原顺序,我们可以把问题看作:在原序列中,寻找尽可能多的 不重叠的 形如 x,y,x,yx,y,x,y 的模式,其中第一个 xx 出现在第一个 yy 之前,第二个 xx 出现在第二个 yy 之前,并且这些四元组按顺序排列(即每个四元组的四个元素在原序列中的下标递增,且不同四元组之间的下标也递增)。

    关键简化:相邻数对

    注意到一个四元组 x,y,x,yx,y,x,y 可以视为 两个相同的相邻数对 (x,y)(x,y) 按顺序出现。即如果我们把原序列中所有相邻元素对 (ai,ai+1)(a_i, a_{i+1}) 看作一个单位,那么一个四元组 x,y,x,yx,y,x,y 对应于两个相邻数对 (x,y)(x,y),它们在原序列中可以不连续(只要第一个数对的第二个元素在第二个数对的第一个元素之前即可)。但是,子序列的选取允许跳过元素,因此如果我们能够找到两个位置 i<ji<j,使得 (ai,ai+1)=(x,y)(a_i,a_{i+1}) = (x,y)(aj,aj+1)=(x,y)(a_j,a_{j+1}) = (x,y),那么我们就可以取出 ai,ai+1,aj,aj+1a_i, a_{i+1}, a_j, a_{j+1} 作为子序列,正好构成 x,y,x,yx,y,x,y。这里要求 i+1<ji+1 < j 以保证顺序(但即使 i+1=ji+1 = j,即两个数对相邻,也是允许的,因为子序列可以取 ai,ai+1,aj,aj+1a_i, a_{i+1}, a_j, a_{j+1} 连续四个元素)。

    因此,问题转化为:在序列的相邻元素对中,找出尽可能多的 相同的无序对(这里是有序对,因为 (x,y)(x,y)(y,x)(y,x) 不同),每个对使用两次,且这些对的使用顺序必须与它们在原序列中出现的顺序一致,且不能重叠(即同一个数组元素不能同时属于两个不同的数对)。实际上,由于我们取的是子序列,每个元素可以被多个数对共享吗?注意:如果我们取第一个数对用了 ai,ai+1a_i, a_{i+1},第二个数对用了 aj,aj+1a_j, a_{j+1},为了保证子序列的顺序,必须有 i+1<ji+1 < j(或者 i<ji < ji+1ji+1 \le j,但 ai+1a_{i+1} 不能与 aja_j 冲突,因为 aja_j 是同一个值也没关系,只要下标不同,子序列允许重复值。然而,如果 i+1=ji+1 = j,那么 ai+1a_{i+1} 同时作为第一个数对的第二个元素和第二个数对的第一个元素,在子序列中同一个位置只能被用一次,因此 i+1=ji+1 = j 时,我们实际上需要取 ai,ai+1,ai+2a_i, a_{i+1}, a_{i+2}?不,我们需要四个元素 ai,ai+1,aj,aj+1a_i, a_{i+1}, a_j, a_{j+1},当 j=i+1j=i+1 时,这四个元素就是 ai,ai+1,ai+1,ai+2a_i, a_{i+1}, a_{i+1}, a_{i+2},这仍然是合法的,因为 ai+1a_{i+1} 出现了两次(一个作为第一个数对的末尾,一个作为第二个数对的开头),但在子序列中下标 i+1i+1 只能出现一次。所以不能重复使用同一个下标。因此我们必须要求 i+1<ji+1 < j,即两个数对之间至少间隔一个位置(不共用元素)。这个问题在贪心处理时需要注意。

    贪心算法

    一种高效的解法如下(见参考代码):

    • 从左到右扫描原序列,维护一个哈希表(或字典)记录 相邻数对 是否已经出现过。
    • 对于每个位置 ii2in2 \le i \le n),考虑数对 (ai1,ai)(a_{i-1}, a_i)
    • 如果该数对已经在哈希表中标记为“出现过一次”,那么我们就找到一个匹配:之前已经有一个相同的 (ai1,ai)(a_{i-1}, a_i) 出现,且那个数对与当前数对不重叠(因为我们每次找到匹配后就删除标记,并且保证中间有间隔)。然后我们输出这四个数:ai1,ai,ai1,aia_{i-1}, a_i, a_{i-1}, a_i(即先前的 x,yx,y 和当前的 x,yx,y),并将该数对的标记清除(表示已使用)。
    • 否则,将该数对的标记设为“出现过一次”,继续扫描。

    为什么这样能保证不重叠?因为一旦我们使用了一个数对(作为四元组的第一个数对),我们立刻将其标记删除,后续不会再使用它。而当前数对是刚扫描到的,它不可能与之前已使用的数对重叠,因为之前已使用的数对在更早的位置,且我们要求 ii 是不断递增的,所以当前数对一定在已使用的数对之后,并且由于我们没有重复使用元素(每次输出四个元素时,我们实际上从原序列中取出了四个位置:假设之前匹配的数对在位置 pp,当前数对在位置 ii,那么取出的四个元素下标为 p,p+1,i,i+1p, p+1, i, i+1,由于 p+1<ip+1 < i(因为在 pp 处匹配后,我们继续扫描 ii,中间跳过了一些元素),因此下标不重叠。但严格来说,如果两个数对刚好相邻(p+1=i1p+1 = i-1?实际上 ppii 的关系:之前匹配的数对是 (ap1,ap)(a_{p-1}, a_p)?需要仔细定义索引。但无论如何,贪心算法在实现时只是简单地顺序扫描,并不显式检查下标是否重叠,然而由于每次匹配后立即删除该数对的记录,并且扫描指针继续前进,自然保证了不会重复使用同一个元素。一个潜在的覆盖问题:如果出现 (x,y)(x,y) 连续出现三次,比如序列 x,y,x,y,x,yx,y,x,y,x,y,那么第一次遇到第一个 x,yx,y 时标记,第二次遇到第二个 x,yx,y 时匹配并输出前四个元素 x,y,x,yx,y,x,y,然后清除标记。第三次遇到第三个 x,yx,y 时,由于标记已清除,它会重新标记,不会与前面冲突。这样我们得到了一组四元组 x,y,x,yx,y,x,y,而剩下的两个元素 x,yx,y 无法配对。这是最优的吗?实际上,最优解应该是取两个四元组:第一个 x,y,x,yx,y,x,y 用位置1-4,第二个 x,y,x,yx,y,x,y 用位置3-6?但位置3和4已经被第一个四元组使用了,不能重复。所以只能取一个四元组。贪心得到的结果就是最优的。

    正确性证明

    引理:将问题转化为选取不相交的相邻数对 (x,y)(x,y),每个数对最多被使用两次(且两次使用必须构成四元组),目标是最大化四元组数量。贪心算法每次遇到一个与之前未配对的相同数对时,立即将其配对,不会影响后续的配对机会。这是因为:

    • 如果当前数对 PP 与之前某个未配对的 QQ 相同,那么配对 QQPP 是最优的,因为 PPQQ 之后第一个能与 QQ 配对的数对。若我们放弃 PP 而等待后面更远的相同数对 RR,则 PP 可能会与更后面的某个数对 SS 配对,但这样 QQPP 之间的间隔更小,可能不会影响总数,但贪心选择 QQPP 与选择 QQRR 相比,前者占用更少的后续空间,不会减少可能的配对总数。更严格地,这是一个区间调度问题的变种:每个数对可以看作一个区间(它的两个下标),要求选取尽可能多的不相交区间对(每个对由两个相同数对组成),贪心取最早结束的匹配是经典的求最大匹配方法。事实上,算法等价于:扫描时,每当遇到一个与之前某个数对相同的数对,就立即将其匹配,这相当于按数对的出现顺序进行匹配,类似于括号匹配。由于每个数对必须使用两个相同实例,且每个实例只能使用一次,贪心匹配能获得最大数量的完整对(类似于找最多不重叠的相同元素对)。因此,该贪心算法能得到最优解。

    算法步骤

    1. 初始化一个空的哈希表(或字典)cntcnt,用于记录每个相邻数对 (x,y)(x,y) 当前是否已出现且未匹配。
    2. 初始化结果序列 ansans 为空。
    3. 遍历 ii22nn(即相邻数对的下标为 (ai1,ai)(a_{i-1}, a_i)):
      • 记当前数对 cur=(ai1,ai)cur = (a_{i-1}, a_i)
      • 如果 cnt[cur]cnt[cur] 为真(即之前出现过一次且未匹配),则:
        • ai1,ai,ai1,aia_{i-1}, a_i, a_{i-1}, a_i 依次加入 ansans
        • cnt[cur]cnt[cur] 置为假(清除标记)。
      • 否则:
        • cnt[cur]cnt[cur] 置为真(标记出现一次)。
    4. 输出 ans|ans| 以及 ansans 中的元素。

    注意:cntcnt 可以用 mapunordered_map 实现,键为 pair<int,int>

    时间复杂度

    • 遍历一次数组,每个数对至多两次操作(插入和查询),哈希表操作平均 O(1)O(1),总复杂度 O(n)O(n)
    • 空间复杂度 O(n)O(n) 用于存储哈希表,但实际最多存储不同数对的数量,不超过 min(n,109)\min(n, 10^9) 实际上 n5×105n \le 5\times 10^5,可行。

    输出格式

    第一行输出 4m4m,即 ansans 的长度;第二行输出 ansans 中的每个整数,用空格分隔。

    示例验证

    例1:a=[3,5,3,5]a = [3,5,3,5]

    • i=2i=2,数对 (3,5)(3,5),未出现过,标记。
    • i=3i=3,数对 (5,3)(5,3),未出现过,标记(注意 (5,3)(5,3)(3,5)(3,5) 不同)。
    • i=4i=4,数对 (3,5)(3,5),已标记,匹配,输出 3,5,3,53,5,3,5。长度 44

    例2:a=[35,1,2,1,2,35,100,200,100,200]a = [35,1,2,1,2,35,100,200,100,200] 扫描过程:

    • (35,1)(35,1) 标记
    • (1,2)(1,2) 标记
    • (2,1)(2,1) 标记
    • (1,2)(1,2) 已标记 -> 输出 1,2,1,21,2,1,2,清除 (1,2)(1,2)
    • (2,35)(2,35) 标记
    • (35,100)(35,100) 标记
    • (100,200)(100,200) 标记
    • (100,200)(100,200) 再次出现 -> 输出 100,200,100,200100,200,100,200,清除 最终得到 [1,2,1,2,100,200,100,200][1,2,1,2,100,200,100,200],长度 88

    符合样例输出。

    总结

    本题的核心是将四元组 x,y,x,yx,y,x,y 转化为两个相同的相邻数对 (x,y)(x,y),然后利用贪心匹配(即每当出现第二次相同的数对时,立即输出一个四元组)来寻找最长子序列。这种贪心策略基于不重叠匹配的思想,保证得到最优解。该解法简单高效,时间复杂度 O(n)O(n),适合 n5×105n \le 5\times 10^5 的数据范围。

    • 1

    信息

    ID
    6910
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者