给定正整数 n 和整数序列 a1,a2,…,a2n,在这 2n 个数中,1,2,…,n 分别各出现恰好 2 次。现在进行 2n 次操作,目标是创建一个长度同样为 2n 的序列 b1,b2,…,b2n,初始时 b 为空序列,每次可以进行以下两种操作之一:
我们的目的是让 b 成为一个回文数列,即令其满足对所有 1≤i≤n,有 bi=b2n+1−i。
请你判断该目的是否能达成,如果可以,请输出字典序最小的操作方案,具体在【输出格式】中说明。
每个测试点包含多组测试数据。
输入的第一行,包含一个整数 T,表示测试数据的组数。对于每组测试数据:
第一行,包含一个正整数 n。
第二行,包含 2n 个用空格隔开的整数 a1,a2,…,a2n。
对每组测试数据输出一行答案。
如果无法生成出回文数列,输出一行 ‐1
,否则输出一行一个长度为 2n 的、由字符 L
或 R
构成的字符串(不含空格),其中 L
表示移除开头元素的操作 1,R
表示操作 2。
你需要输出所有方案对应的字符串中字典序最小的一个。
字典序的比较规则如下:
长度均为 2n 的字符串 s1∼2n 比 t1∼2n 字典序小,当且仅当存在下标 1≤k≤2n 使得对于每个 1≤i 输入 #1: 输出 #1: 输入 #2: 输出 #2: 【样例解释 #1】 在第一组数据中,生成的的 b 数列是 [4, 5, 3, 1, 2, 2, 1, 3, 5, 4],可以看出这是一个回文数列。 另一种可能的操作方案是 【数据范围】 令 ∑n 表示所有 T 组测试数据中 n 的和。 对所有测试点保证 1≤T≤100,1≤n,∑n≤5×10^5。 特殊性质: 如果我们每次删除 a 中两个相邻且相等的数,存在一种方式将序列删空(例如 a = [1, 2, 2, 1])。 palin.zip 4.25KB 这个题解思路清奇(就是暴力加上了一堆优化然后跑过了),相较于 std 可能会有所不同。 首先一看,回文,好耶, Hash —— 然而并不是。 众所周知,了解一道题的最好方法就是手推样例(瞎说的),所以我们来手推一下。 样例一的第一组数据的 a 和 b: 然后手模一下怎么生成 b 的: 这当然是不可以的,但是这样看确实看不出来什么东西。 想一下题上有什么提示…… b 是一个回文数列。 既然 b 是一个好观察的回文数列那就从 b 入手吧。 b 前后相等,那就把前后分开看看? 好像也没有什么。 不对,看一看后半段 b 。 这是由连续的一段 a 组成的。 想一想,不论什么 b 的后半段都是由 a 中一段连续的,长度为 n 的数列组成的。 就像这样: (长度不准确请忽视) 有了这个,我们就可以开始我们优雅の暴力了。 1.暴力枚举每一个后半段区间位置。 2.判断是否合法。 3.输出。 然后这个时候有人就会跑上来说,你这个暴力是 O(n^3) 的啊不是 O(n^2) 的啊。 ( n^3 : 枚举 n 个区间、记录每个区间 n 个数,判断是否合法要 n 次判断) 所以我们需要对暴力进行优化。 首先我们想,一个后半段区间假若有机会成为解,那么必要条件是…… 当然是这个区间里包含了 1,2,...,n 啦(不然你怎么回文)。 那么我们在枚举一个后半段区间时可以顺便来看看它是否包含了 1,2,...,n,没有则直接跳到下一个区间,否则来判断是否合法以及假如合法则方案是否是字典序最小的。 当然这个优化对于随机数据收益巨大,能大幅消减判断的 n 以至于能看成 O(n^2) ,因为减少了很多不必要的判断;但是对于构造的数据,比如说这个: 1,2,...,10000,1,2,...,10000 那这个优化就什么用都没有,直接退化 O(n^3)。 所以再来一个显而易见的优化:每个区间都是在上一个区间基础上挪了一个罢了,所以可以直接在上一个区间的基础上进行改动,这样就把记录每个区间的数的 n 就消掉了,这样就是一个完全的 O(n^2) 暴力了。加上前面的优化,对于随机数据能直接跑到近似于 O(n) 乘上一个大常数。 判断可行性解释:用两个栈记录左边前半部分与右边前半部分,然后和后半部分尝试匹配。每匹配成功就把它们加入maybeans方案中。对于这个“匹配”,请好好读读,因为后半段优化是基于这个“匹配”的。 代码解释: in 数组是用来记录区间内所有数的个数, cnt 是用来记录区间内有多少个不同的数;对于 in 和 cnt 的变化规律为什么是这样,手推一遍就很容易明白。然后是让每个区间生成最优解:因为我们需要让操作 R 尽可能在后,那么我们就先让 前半部分的右端先进行匹配,这样对于每个枚举的后半段区间,生成的答案就是局部最优解 然后机房大佬 FJN 因为没有打出暴力 T3 感到非常悲伤,然后听说我打出了一个随机化数据能跑 O(n) ,构造能跑 O(n^2) 的暴力于是来找我看看代码。 看着看着然后他就来了一句:你这个可以再优化成 O(n) 的。 对,这就是最后一个优化。 概括一下就是:倒序枚举后半段区间,当找到第一个解时就一定是最优解。 为甚么呢? 首先我们想一个基础事实:假如 a 中有 2 个或以上的符合要求的后半段区间,那么它们一定是有重合部分的。 (例外: 1,2,3,1,2,3 中前一个 1,2,3 和后一个 1,2,3 不重合,但是看一眼就知道选后面一个答案更优。) 就像这样: 为什么会重合?因为两段区间都包含 1 至 n 中所有数,而 1 至 n 每个又只有两个,所以一定会重合。 上图的红绿线段就是两个符合要求的后半段区间,那么显而易见的可以知道,没有重叠的部分是相同的。(你问为什么? A-B=A-B ) 那么,在进行回文配对时,红色没有重叠部分一定是跟绿色没有重叠部分进行配对。毕竟含有相同的数。 那么,假若我们选择红色作为后半段区间,那么绿色的未重叠部分则是用 R 操作弹出。(假如可行) 假若我们选择绿色作为后半段区间,那么红色的未重叠部分则是用 L 操作弹出。(假如可行) 回看匹配过程,不管我们选择红色还是绿色,我们一定是先将不属于红色或绿色的是跟红绿重合部分匹配(你不把它们匹配完怎么匹配中间的啊),那么它们的在方案中位置及方案是固定的,那么就剩红色和绿色不重叠部分了。 假如我们选择是红色且红色可行,那么在这里我们会全部用 R 进行剩余匹配。 假如是绿色且绿色可行,那么在这里我们会全部用 L 进行剩余匹配。 显而易见的,绿色更优。 所以两段后半部分区间都可行时,那么选择后面那段作为后半区间更优。 于是乎,倒着枚举区间,遇到第一个可行的区间就输出答案,正确性证明完毕,于是就开心滴砍掉了大部分判断这个 n 。结合前面的优化,一个近似于 O(n) 就这么搞出来啦~ 当然,如果真的要专门构造数据来卡当然是可以退化到 O(n^2) 的(悲) 不过能从暴力n^3优化到n,这是一个非常难的过程。 [CSP-S 2021] 回文 - 洛谷https://www.luogu.com.cn/problem/P7915 输入输出样例
2
5
4 1 2 4 5 3 1 2 3 5
3
3 2 1 2 1 3
LRRLLRRRRL
-1
见附件中的 palin/palin2.in
见附件中的 palin/palin2.ans
说明/提示
LRRLLRRRRR
,但比答案方案的字典序要大。
测试点编号
T≤
n≤
∑n≤
特殊性质
1∼7
10
10
50
无
8∼10
100
20
1000
无
11∼12
100
100
1000
无
13∼15
100
1000
25000
无
16∼17
1
5×10^5
5×10^5
无
18∼20
100
5×10^5
5×10^5
有
21∼25
100
5×10^5
5×10^5
无
附件下载:
思路:
嗯,连得密密麻麻的不想看,做 T4 去了。优化一:
优化二
(民间数据能 AC ,不得不说真的有点水) 现在只有 80 分了悲伤优化二代码:
#include
优化三
完整代码:
#include
总结:
比 T2 简单的 T3 这辈子不多了……是名副其实的签到题……题目链接: