[CSP-S 2021] 回文
Description
给定一个数列,长度为 2n ,其中 [1, n] 所有数均出现两次。
现在要构造一个新数列,有如下要求:
-
从原数列开头取数,加入到新数列末尾,答案输出“ L ”;
-
从原数列末尾取数,加入到新数列末尾, 答案输出“ R ”。
问能否存在一种构造方案,使得新数列为一个会问序列。
如果有多种方案,输出答案的字典序最小的那一种。
多测。
\(1\leq t \leq 100\ \ \ 1\leq \sum{n} \leq 5\cdot 10 ^ 5\)
Analysis
考场上的时候,开这道题的时候,第一感觉其实是有点诈骗。
因为开头和结尾,无论怎么构造,总有一个在新数列的开头,这样的话,无论怎么样,开头或结尾在原数列对应的数字相同的另一个地方必在结尾。
假设我们去掉的是开头,这样其实就相当于将原数列去掉开头和其对应的另一个地方,一分为二。
那么这两数列的开头和结尾又是和原数列一样的情况,总有一个要接在开头的后面,一个接在结尾的前面,那么就去比对,相同就填,不相同就无解,为了让字典序尽可能小,贪心地能“ L ”就“ L ”的去填就好了。
Solution
其实 Analysis 里面说的很笼统,基本意思是到了,就是不太好懂,这里的话就配上个图详细地嗦一两下。
首先,原数列开头和结尾无论怎么填,总有一个是新数列的开头。那我们还是假设选的开头先填,并且也找到了开头对应的位置,那么现在的情况大概就是:
也就是说,我们定下来新数列开头结尾的数值以后,原数列会像这样被劈成两半。那么上面那个数列的两个紫色三角所在处,无论怎么填总会有一个会接在开头的后面。同时,对应的两个蓝色三角处,必有一个会接在结尾的前面。
同理这样递归下去,就可以找到一组合法解,并且假如有个地方不能继续填下去,必定是无解了。
那么怎么保证字典序最小呢?
因为能发现,这样剖成两个数列之后,前面那一段只能“ L ”,后面那一段只能“ R ”(因为都被结尾填的数给阻挡了),所以匹配成功以后,这个地方的走法是唯一的。
所以只有存在上述两个紫色三角对应的两个蓝色三角能有两种匹配方式时,答案才会有影响,此时只用先“ L ”后“ R ”就可以了。
那会不会出现这样子处理后导致误解了呢,肯定是不会的,因为这只是调整了加入的顺序,从根本上这两组是迟早要加的,并不影响答案是否有解。
(最后有个小细节,就是如果原数列开头结尾是一样的话,只肯能是全部“ L ”,可以选择特判一下,虽然上述做法也能搞定)
那么其实对于先加入结尾,是一个道理的。
Code
(维护的话可以直接上指针,但由于考场上我有点糊涂,手写了个deque,麻烦了亿点点)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int t, n, a[N], id1, id2, ans1[N], ans2[N];
//deque<int> q1, q2, p1, p2;
char a1[N], a2[N];
inline int read() {
char ch = getchar();
int s = 0, w = 1;
while (ch < '0' || ch > '9') {if(ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
return s * w;
}
struct mdzz {
int l, r, q[N];
inline int size() {
return r - l;
}
inline void pop_back() {
--r;
}
inline void pop_front() {
++l;
}
inline bool empty() {
return (l == r);
}
inline int front() {
return q[l + 1];
}
inline int back() {
return q[r];
}
inline void push_back(int x) {
q[++r] = x;
}
} q1, q2, p1, p2;
inline int solve1() {
int id = 2;
while ((!q1.empty()) || (!p1.empty())) {
if (q1.empty()) {
while (!p1.empty()) {
int u2 = a[p1.back()], v2 = a[p1.front()];
if (u2 == v2) {
ans1[id] = ans1[n - id + 1] = u2;
a1[id] = a1[n - id + 1] = 'R';
++id;
p1.pop_back(); p1.pop_front();
}
else {
return -1;
}
}
return 0;
}
if (p1.empty()) {
while (!q1.empty()) {
int u1 = a[q1.back()], v1 = a[q1.front()];
if (u1 == v1) {
ans1[id] = ans1[n - id + 1] = u1;
a1[id] = a1[n - id + 1] = 'L';
++id;
q1.pop_back(); q1.pop_front();
}
else {
return -1;
}
}
return 0;
}
int u1 = a[q1.back()], u2 = a[p1.back()];
int v1 = a[q1.front()], v2 = a[p1.front()];
if (u1 == v1 && v1 == u2 && u2 == v2) {
ans1[id] = ans1[n - id + 1] = u1;
a1[id] = 'L'; a1[n - id + 1] = 'R';
if (q1.back() + 1 == p1.back()) a1[n - id + 1] = 'L';
++id;
return 0;
//有坑虽然不会出现,但还是写上。。
}
if (u1 == v1 && q1.size() > 1) {
ans1[id] = ans1[n - id + 1] = u1;
a1[id] = a1[n - id + 1] = 'L';
++id;
q1.pop_front(); q1.pop_back();
}
else if (u2 == v1) {
ans1[id] = ans1[n - id + 1] = u2;
a1[id] = 'L', a1[n - id + 1] = 'R';
++id;
q1.pop_front(); p1.pop_back();
}
else if (u1 == v2) {
ans1[id] = ans1[n - id + 1] = u1;
a1[id] = 'R', a1[n - id + 1] = 'L';
++id;
p1.pop_front(); q1.pop_back();
}
else if (u2 == v2 && p1.size() > 1) {
ans1[id] = ans1[n - id + 1] = u2;
a1[id] = a1[n - id + 1] = 'R';
++id;
p1.pop_front(); p1.pop_back();
}
else {
return -1;
}
}
return 0;
}
inline int solve2() {
int id = 2;
while ((!q2.empty()) || (!p2.empty())) {
if (q2.empty()) {
while (!p2.empty()) {
int u2 = a[p2.back()], v2 = a[p2.front()];
if (u2 == v2) {
ans2[id] = ans2[n - id + 1] = u2;
a2[id] = a2[n - id + 1] = 'R';
++id;
p2.pop_back(); p2.pop_front();
}
else {
return -1;
}
}
return 0;
}
if (p2.empty()) {
while (!q2.empty()) {
int u1 = a[q2.back()], v1 = a[q2.front()];
if (u1 == v1) {
ans2[id] = ans2[n - id + 1] = u1;
a2[id] = a2[n - id + 1] = 'L';
++id;
q2.pop_back(); q2.pop_front();
}
else {
return -1;
}
}
return 0;
}
int u1 = a[q2.back()], u2 = a[p2.back()];
int v1 = a[q2.front()], v2 = a[p2.front()];
if (u1 == v1 && v1 == u2 && u2 == v2) {
ans2[id] = ans2[n - id + 1] = u1;
a2[id] = 'L'; a2[n - id + 1] = 'R';
if (q2.back() + 1 == p2.back()) a2[n - id + 1] = 'L';
++id;
return 0;
//有坑虽然不会出现,但还是写上。。
}
if (u1 == v1 && q2.size() > 1) {
ans2[id] = ans2[n - id + 1] = u1;
a2[id] = a2[n - id + 1] = 'L';
++id;
q2.pop_front(); q2.pop_back();
}
else if (u2 == v1) {
ans2[id] = ans2[n - id + 1] = u2;
a2[id] = 'L', a2[n - id + 1] = 'R';
++id;
q2.pop_front(); p2.pop_back();
}
else if (u1 == v2) {
ans2[id] = ans2[n - id + 1] = u1;
a2[id] = 'R', a2[n - id + 1] = 'L';
++id;
p2.pop_front(); q2.pop_back();
}
else if (u2 == v2 && p2.size() > 1) {
ans2[id] = ans2[n - id + 1] = u2;
a2[id] = a2[n - id + 1] = 'R';
++id;
p2.pop_front(); p2.pop_back();
}
else {
return -1;
}
}
return 0;
}
inline void print1() {
for (int i = 1; i <= n; ++i) {
cout << a1[i];
}
printf("\n");
}
inline void print2() {
for (int i = 1; i <= n; ++i) {
cout << a2[i];
}
printf("\n");
}
inline void clear() {
q1.l = q2.l = p1.l = p2.l = 0;
q1.r = q2.r = p1.r = p2.r = 0;
}
inline void mian() {
clear();
n = read() * 2;
for (int i = 1; i <= n; ++i) {
ans1[i] = ans2[i] = 0;
a[i] = read();
}
id1 = a[1]; id2 = a[n];
if (id1 == id2) {
for (int i = 1; i <= n; ++i) {
if (a[i] != a[n - i + 1]) {
printf("-1\n");
return ;
}
}
for (int i = 1; i <= n; ++i) printf("L");
printf("\n");
return ;
}
for (int i = 2; i <= n; ++i) {
if (a[i] == id1) {
for (int j = 2; j < i; ++j) q1.push_back(j);
for (int j = n; j > i; --j) p1.push_back(j);
ans1[1] = ans1[n] = id1;
a1[1] = 'L'; a1[n] = 'L';
break;
}
}
for (int i = 1; i < n; ++i) {
if (a[i] == id2) {
for (int j = 1; j < i; ++j) q2.push_back(j);
for (int j = n - 1; j > i; --j) p2.push_back(j);
ans2[1] = ans2[n] = id2;
a2[1] = 'R'; a2[n] = 'L';
break;
}
}
if (solve1() != -1) {
print1();
}
else if (solve2() != -1) {
print2();
}
else printf("-1\n");
}
int main() {
// freopen("palin.in", "r", stdin);
// freopen("palin.out", "w", stdout);
t = read();
while (t--) mian();
return 0;
}