[CSP-S2021-T3][题解] 回文
Solution
在一个长度为 的序列 中,可以拿出序列最左边,最右边的数。放到 序列的末尾,使得 序列回文。
以下对先选左边的数进行讨论。先选右边的数同理。
若已选了一个数,因为要求回文,那么最后一个数也会被确定。假设最后一个数在序列 的第 个。
现在我们要选第二个数。有两种选择:最左边的,最右边的数,分别设为
设与它们相等的两个数是第 个数。以下对 进行讨论, 同理。
-
当 时,仅当 时可以成立。因为 是倒数第二个要选的数, 是倒数第一个要选的数。如果 之间有其他数,就要在他们之前选,只能从最左,最右边的数没法满足条件。所以必须要相邻,即
-
当 时,同理可得,两数必须相邻。所以当 ,可以成立。
此时我们将问题一般化,由于倒数选取的数必须相邻,我们将倒数的记作 。
此时,当且仅当选取第 个数满足 或 时,才满足条件。分别记录最左,最右边的 ,记作 。
如果选取的两端都满足,不必担心选择一边可以,另一边不行。只需要考虑字典序,即选左边的数就可以。
证明
现在要选 。有两种情况:
-
。如果选 ,。此时仍类似于该序列的“三段式结构”。依旧这样判断,如果一直为这种条件,那么直接成立。如果不为,那么就会转化为一般情况。如果选 ,同样直接成立或为一般情况。对于一般情况,如果不能满足,就会在 出现同时不满足。而这种情况是无论选哪边,最终都要达到的。所以只关心字典序即可。
-
。同理可得,只与字典序有关。
Code
#include<bits/stdc++.h>
using namespace std;
#define ck(x) (x==miv-1||x==mv+1)
const int N=1e6+10;
int T,a[N],n,p[N],v[N],mv,miv,t[N];
string ans1;
int go(int s,int l,int r) {
memset(v,0,sizeof v);
mv=miv=p[s];v[p[s]]=1;
for(int i=2;i<=n;i++) {
int L=p[l],R=p[r];
if(ck(L)&&v[l]==0&&v[L]==0) l++, ans1+="L", miv=min(miv,L), mv=max(mv,L), v[L]=i;
else if(ck(R)&&v[r]==0&&v[R]==0) r--, ans1+='R', miv=min(miv,R), mv=max(mv,R), v[R]=i;
else return 0;
}
return 1;
}
void print(int l,int r) {
cout<<ans1;
while(l<=r)
if(v[l]>=v[r]) cout<<"L", l++;
else cout<<"R", r--;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--) {
memset(t,0,sizeof t);
cin>>n;
for(int i=1;i<=2*n;i++) {
cin>>a[i];
if(t[a[i]]==0) t[a[i]]=i;
else p[i]=t[a[i]],p[t[a[i]]]=i;
}
if(ans1="L",go(1,2,2*n)) print(miv,mv);
else if(ans1="R",go(2*n,1,2*n-1)) print(miv,mv);
else cout<<-1;
cout<<"\n";
}
return 0;
}