题目大意
给定一个长度为 \(2n\) 的序列 \(a\),要求每次取出其第一个数或者最后一个数,使得取出的数列 \(b\) 为一个回文数列。
注:回文数列即 \(\forall i\in \{ 1,n \}\) 都有 \(b_i=b_{2n-i+1}\)
Sol
考虑第一步先取出 \(a\) 左边的那个元素,则右边同理。
如果取出左边元素,则 \(b_1=a_1\),那么 \(a\) 中与 \(a_1\) 对应的另一个元素一定最后取出,下标记为 \(p\)。
可以把 \(a\) 划分成两边,即序列 \(c\) \([2,p-1]\) 和序列 \(d\) \([p+1,2n]\)。
想要从其中取出元素,一定需要一个元素在这两个序列的首尾出现两次,如果有两个元素符合条件,优先选 \(c\) 中的元素(字典序最小)。
那么记当前是第 \(i\) 步,当前的操作应该存储在操作序列的 \(i\) 和 \(2n-i+1\) 这两个位置。
(建议自己模拟一下)
接下来是分讨:
- \(c\) 的左右端相等,前为
L
,后为L
- \(c\) 的左端与 \(d\) 的左端相等,前为
L
,后为R
- \(c\) 的右端与 \(d\) 的右端相等,前为
R
,后为L
- \(d\) 的左右端相等,前为
R
,后为R
- 其他情况无解,请自行考虑为什么无解
Code
#include <cstdio>
#include <iostream>
#include <algorithm>using namespace std;const int N = 1e6+10;int n;
int a[N];
char res1[N] , res2[N];bool calc(char res[] , int cl , int cr , int dl , int dr) {for(int i = 2 ; i <= n/2 ; i ++) {if(cl < cr && a[cl] == a[cr]) {res[i] = 'L'; res[n-i+1] = 'L';cl ++; cr --;continue;}if(cl <= cr && dl <= dr) {if(a[cl] == a[dl]) {res[i] = 'L'; res[n-i+1] = 'R';cl ++; dl ++;continue;}if(a[cr] == a[dr]) {res[i] = 'R'; res[n-i+1] = 'L';cr --; dr --;continue;}}if(dl < dr && a[dl] == a[dr]) {res[i] = 'R'; res[n-i+1] = 'R';dl ++; dr --;continue;}return false;}return true;
}void slove() {cin >> n; n <<= 1;for(int i = 1 ; i <= n ; i ++)cin >> a[i];int p1 = 0 , p2 = 0;for(int i = 1 ; i <= n ; i ++) {if(i > 1 && a[i] == a[1]) p1 = i;if(i < n && a[i] == a[n]) p2 = i;if(p1 && p2) break;}res1[1] = 'L'; res1[n] = 'L'; res1[n+1] = '\0';res2[1] = 'R'; res2[n] = 'L'; res2[n+1] = '\0';if(calc(res1 , 2 , p1-1 , p1+1 , n)) cout << res1+1 << '\n';else if(calc(res2 , 1 , p2-1 , p2+1 , n-1)) cout << res2+1 << '\n';else cout << -1 << '\n';return;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);int T; cin >> T;while(T --) slove();return 0;
}
(比较丑陋,见谅)