luogu P7915 [CSP-S 2021] 回文
题目大意
给定一个长度为 \(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;
}
(比较丑陋,见谅)