1. 程式人生 > 其它 >[CSP-S 2021] 迴文

[CSP-S 2021] 迴文

迴文

吐槽:這題的字典序最小是答案字串的字典序最小。

題目大意

給定一個長為 \(2n\) 的序列,由數字 \(1~n\) 組成,且每個數字有兩個。

每次操作要麼選擇左端點要麼選擇右端點,選擇後將該數字從序列裡刪除並加入另一個序列的末尾,要求最後得到的序列是一個迴文串。

輸出一個長為 \(2n\) 的字串,為每一次的選擇,選左端點為 \(L\) ,選右端點為 \(R\)

若有多種方案,輸出字典序最小的那一個。

分析

手玩一下樣例,可以發現以下性質:

  • 每當我們取一個數,我們必定能在序列中間的某個位置找到與這個數相同的數。

  • 由於是迴文串,我們可以將其分為兩半取,先考慮前一半,發現前一半越先取後一半就要越後取。

於是,我們能夠推出一個比較重要的性質。

假設我們取了一個數,在中間某個位置找到了它,又取了另外一個數,同樣在中間某個位置找到了和它相同的那個數。這兩個對應位置如果不相連,必然可以相信,它們中間的數會在未來某個位置被我們取到,根據上述性質 \(2\) ,我們要在兩個後取的數的包裹下取一個先取的數,顯然是不合題意的。

所以,我們可以把序列分為兩部分,一部分是端點,一部分是已經選的數的對映區間,要保證,該區間必須連續,所以每次選的數必須要在這個區間的兩邊擴充套件,也就是說只能選這個區間的兩邊對應的數。簡單的,如果無法進行擴充套件,說明我們的匹配失敗了。

我們可以維護四個指標,然後能選左就選左,否則考慮選右,由於最開始我們沒有區間,於是我們進行兩次這個操作,第一次最開始選擇左端點,第二次最開始選擇右端點,若第一次已經成功那麼就是最優解,否則進行第二次。
若兩次都失敗,則無解。

CODE

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;	
}
int T,n;
int R[N],a[2*N];
int num[N];
char ans[2*N],temp[2*N];
bool flag;
inline void Solve(int x,int y,int A,int B) //記錄目前的位置 
{
	int cnt=1;
	while(cnt<n){ //選完為止
		//理論上兩邊都能選
		//先選小的
		int tx=x;
		if(a[x]==a[B]&&x<=A&&B<=y) B++,x++;
		else if(a[x]==a[A]&&x<A) A--,x++;
		else if(a[y]==a[B]&&B<y) B++,y--;
		else if(a[y]==a[A]&&x<=A&&B<=y) A--,y--;
		else return;
		if(tx!=x) temp[++cnt]='L';
		else temp[++cnt]='R';
	}
	flag=true; //匹配成功 
}
inline void out()
{
	int x=1,y=2*n,tot=0;
	for(register int i=1;i<=n;i++){
		if(temp[i]=='L') R[++tot]=a[x],x++;
		else R[++tot]=a[y],y--;
	}
	for(register int i=n+1;i<=2*n;i++){
		if(R[tot]==a[x]) temp[i]='L',x++;
		else temp[i]='R',y--;
		tot--;
	}
	for(register int i=1;i<=2*n;i++){
		if(ans[i]<temp[i]) return;
		if(temp[i]<ans[i]) break;
	}
	for(register int i=1;i<=2*n;i++) ans[i]=temp[i];
}
inline void update()
{
	
	flag=false;
	int p;
	for(register int i=2;i<=2*n;i++)
		if(a[i]==a[1]) { p=i; break; }
	temp[1]='L',Solve(2,2*n,p-1,p+1);
	if(flag) { out(); return; }
	for(register int i=1;i<=2*n-1;i++)
		if(a[i]==a[2*n]) { p=i; break; }
	temp[1]='R',Solve(1,2*n-1,p-1,p+1); //從兩個位置出發做一遍 
	if(flag) { out(); return; }
}
int main()
{
//      freopen("palin.in","r",stdin);
//      freopen("palin.out","w",stdout);
	T=read();
	while(T--){
		ans[1]='Z'; //初始值 
		n=read();
		for(register int i=1;i<=2*n;i++) a[i]=read();
		update();
		if(ans[1]=='Z') printf("-1\n");
		else{
			for(register int i=1;i<=2*n;i++) printf("%c",ans[i]);
			printf("\n");
		}
	}
	return 0;
}
作者: ╰⋛⋋⊱๑落葉๑⊰⋌⋚╯

-------------------------------------------

海到無邊天作岸,山登絕頂我為峰!