[CSP-S2021-T3][题解] 回文

原题链接

Solution

在一个长度为 2×n2\times n 的序列 aa 中,可以拿出序列最左边,最右边的数。放到 bb 序列的末尾,使得 bb 序列回文。


以下对先选左边的数进行讨论。先选右边的数同理。

若已选了一个数,因为要求回文,那么最后一个数也会被确定。假设最后一个数在序列 aa 的第 KK 个。

现在我们要选第二个数。有两种选择:最左边的,最右边的数,分别设为 x,yx,y

设与它们相等的两个数是第 p[x],p[y]p[x],p[y] 个数。以下对 x,p[x]x,p[x] 进行讨论,y,p[y]y,p[y] 同理。

  • p[x]<kp[x] < k 时,仅当 p[x]=k1p[x]=k-1 时可以成立。因为 p[x]p[x] 是倒数第二个要选的数,KK 是倒数第一个要选的数。如果 p[x],xp[x],x 之间有其他数,就要在他们之前选,只能从最左,最右边的数没法满足条件。所以必须要相邻,即 p[x]=k1p[x]=k-1

  • p[x]>kp[x]> k 时,同理可得,两数必须相邻。所以当 p[x]=k+1p[x]=k+1,可以成立。


此时我们将问题一般化,由于倒数选取的数必须相邻,我们将倒数的记作 xx

a1  a2  a3  ...  ai  x  x  ...  x  x  aj  aj+1  ...  a2×n1  a2×na_1\ \ a_2\ \ a_3\ \ ...\ \ a_i\ |\ x\ \ x\ \ ...\ \ x\ \ x\ |\ a_j\ \ a_{j+1}\ \ ...\ \ a_{2\times n-1}\ \ a_{2\times n}

此时,当且仅当选取第 ww 个数满足 w=jw=jw=iw=i 时,才满足条件。分别记录最左,最右边的 xx,记作 mi,mami,maimi1,jma+1i\gets mi-1,j\gets ma+1

如果选取的两端都满足,不必担心选择一边可以,另一边不行。只需要考虑字典序,即选左边的数就可以。

证明

a1  a2  a3  ...  ai  x  x  ...  x  x  aj  aj+1  ...  a2×n1  a2×na_1\ \ a_2\ \ a_3\ \ ...\ \ a_i\ |\ x\ \ x\ \ ...\ \ x\ \ x\ |\ a_j\ \ a_{j+1}\ \ ...\ \ a_{2\times n-1}\ \ a_{2\times n}

现在要选 al,ara_l,a_r。有两种情况:

  • p[al]=ai,p[ar]=ajp[a_l]=a_i,p[a_r]=a_j。如果选 ala_laixa_i\gets x。此时仍类似于该序列的“三段式结构”。依旧这样判断,如果一直为这种条件,那么直接成立。如果不为,那么就会转化为一般情况。如果选 ara_r,同样直接成立或为一般情况。对于一般情况,如果不能满足,就会在 ap,aqa_p,a_q 出现同时不满足。而这种情况是无论选哪边,最终都要达到的。所以只关心字典序即可。

  • p[al]=aj,p[ar]=aip[a_l]=a_j,p[a_r]=a_i。同理可得,只与字典序有关。

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;
}
posted @ 2023-09-07 13:59  cjrqwq  阅读(8)  评论(0编辑  收藏  举报  来源