題目描述:
給定正整數 n 和整數序列 a1, a2,…,a2n,在這 2n 個數中,1, 2,…,n 分別各出現恰好 2 次。現在進行 2n 次操作,目標是創建一個長度同樣為 2n 的序列 b 1,b2,…,b2n,初始時 b 為空序列,每次可以進行以下兩種操作之一: 將序列 a 的開頭元素加到 b 的末尾,並從 a 中移除。 將序列 a 的末尾元素加到 b 的末尾,並從 a 中移除。 我們的目的是讓 b 成為一個回文數列,即令其滿足對所有 1≤i≤n,有 bi=b2n+1−i。 請你判斷該目的是否能達成,如果可以,請輸出字典序最小的操作方案,具體在【輸出格式】中說明。
輸入格式:
每個測試點包含多組測試數據。
輸入的第一行,包含一個整數 T,表示測試數據的組數。對於每組測試數據:
第一行,包含一個正整數 n。
第二行,包含 2n 個用空格隔開的整數 a1,a2,…,a2n 。
輸出格式:
對每組測試數據輸出一行答案。 如果無法生成出回文數列,輸出一行 ‐1,否則輸出一行一個長度為 2n 的、由字符 L 或 R 構成的字符串(不含空格),其中 L 表示移除開頭元素的操作 1,R 表示操作 2。 你需要輸出所有方案對應的字符串中字典序最小的一個。 字典序的比較規則如下:長度均為 2n 的字符串 s 1∼2n 比 t 1∼2n字典序小,當且僅當存在下標 1≤k≤2n 使得對於每個 1≤i<k 有 s i=t i 且 s k < t k。
數據范圍:

一道有趣的構造題。
以左端點為例分析一下:

假設紅圈出數值相同,那么顯然對於 左邊一半 會 從左往右 取,右邊一半 會 從右往左 取(如綠色箭頭)。這樣出來的答案是正着的。

如果我們從中間開始往兩側去(如黃色箭頭),這樣出來的答案正好是 倒的。

現在我們可以取的是左右端點(即綠色點),那么,如果有解,其對應點一定位於中間點的相鄰兩側(即藍色點)。
想到這,就很簡單了。
由於要字典序最小,所以優先考慮取左端點,若無解再考慮右端點,若還無解則改題無解。
(詳見代碼)
Code:
回文
UpData(2021.11.1):
以上做法(包括Code)均可通過民間數據,但官方數據對此做法進行了一定的hack:構造一些 “看上去” 可行的無解情況,讓搜索樹跑滿。
這個時候我們可優化一下,比如剪枝,當然計時也是可以的(我是計的時)。
New Code:
#include<bits/stdc++.h> using namespace std; int Step,_,n,A[1000005],Nxt[1000005],Lst[1000005],Ans[2][500005]; #define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) char buf[1<<21],*p1,*p2; inline int read() { char ch;int x(0);while((ch=gc)<48); do x=x*10+ch-48;while((ch=gc)>=48); return x; } inline bool DFS(int a,int b,int c,int d) { if(++Step>500000) return 0; if(a>b&&c>d) return 1; if(a<=b) { if(a!=b&&A[a]==A[b]&&DFS(a+1,b-1,c,d)) {Ans[0][++Ans[0][0]]=1,Ans[1][++Ans[1][0]]=1;return 1;} if(c<=d&&A[a]==A[c]&&DFS(a+1,b,c+1,d)) {Ans[0][++Ans[0][0]]=1,Ans[1][++Ans[1][0]]=2;return 1;} } if(c<=d) { if(a<=b&&A[d]==A[b]&&DFS(a,b-1,c,d-1)) {Ans[0][++Ans[0][0]]=2,Ans[1][++Ans[1][0]]=1;return 1;} if(c!=d&&A[d]==A[c]&&DFS(a,b,c+1,d-1)) {Ans[0][++Ans[0][0]]=2,Ans[1][++Ans[1][0]]=2;return 1;} } return 0; } inline void Print(int x) { if(x==1) printf("L"); else printf("R"); for(register int i=Ans[0][0];i;--i) if(Ans[0][i]==1) printf("L") ; else printf("R"); for(register int i=1;i<=Ans[1][0];++i) if(Ans[1][i]==1) printf("L"); else printf("R"); printf("L\n"); } int main() { _=read(); for(register int __=1;__<=_;++__) { n=read(),Step=0;for(register int i=1;i<=n;++i) Nxt[i]=Lst[i]=0;Ans[0][0]=Ans[1][0]=0,n*=2; for(register int i=1;i<=n;++i) { A[i]=read(); if(Nxt[A[i]]) Lst[A[i]]=i; else Nxt[A[i]]=i; } if(DFS(2,Lst[A[1]]-1,Lst[A[1]]+1,n)) Print(1); else if(DFS(1,Nxt[A[n]]-1,Nxt[A[n]]+1,n-1)) Print(2); else printf("-1\n"); } return 0; }
