题目本来是挺简单的,可惜我的常数太大,用了各种黑科技才A掉
把$A$和$B$拼到一起,中间和最后加一个无穷大的分隔符,求出后缀数组之后根据$rank$贪心取即可
如果不在末尾加分隔符,可以看一看这组数据
3
2 1 1
2
2 1
拼起来后是$[2,1,1,\infty,2,1]$,此时后缀$[2,1]$小于后缀$[2,1,1,\infty,2,1]$,先取了$[2,1]$,然而更优解是先取$[2,1,1,\infty,2,1]$
如果在末尾加一个分隔符,拼起来后是$[2,1,1,\infty,2,1,\infty$,此时后缀$[2,1,1,\infty,2,1,\infty]$就排在$[2,1,\infty]$前面了
这样做的原理是假如当前取到$A,B$,$A$是$B$的前缀且$A$是第二个字符串的后缀,那么如果$B$比$A$多的部分都是很小的数,先把$B$取完是更优的,也就是说我们让$A$的末尾变为无穷大,那么就可以让$B$排在$A$前面
代码常数太大,用了输入输出优化才卡过去


???
#include<stdio.h>
#include<string.h>
int s[400010],rk[800010],cnt[400010];
struct pr{
int c[2],id;
pr(int a=0,int b=0,int d=0){c[0]=a;c[1]=b;id=d;}
}p[400010],q[400010];
bool operator!=(pr a,pr b){return a.c[0]!=b.c[0]||a.c[1]!=b.c[1];}
int max(int a,int b){return a>b?a:b;}
void sort(int n,int f){
int i,m=0;
memset(cnt,0,sizeof(cnt));
for(i=1;i<=n;i++){
cnt[p[i].c[f]]++;
m=max(m,p[i].c[f]);
}
for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(i=n;i>0;i--)q[cnt[p[i].c[f]]--]=p[i];
for(i=1;i<=n;i++)p[i]=q[i];
}
void suf(int n){
int i,l,m;
for(i=1;i<=n;i++)rk[i]=s[i];
for(l=1;l<=n;l<<=1){
for(i=1;i<=n;i++)p[i]=pr(rk[i],rk[i+l],i);
sort(n,1);
sort(n,0);
m=0;
for(i=1;i<=n;i++){
if(p[i]!=p[i-1])m++;
rk[p[i].id]=m;
}
}
}
int rd(){
char c;
int x=0;
for(c=getchar();c>‘9‘||c<‘0‘;c=getchar());
x=c-‘0‘;
for(c=getchar();c<=‘9‘&&c>=‘0‘;c=getchar())x=x*10+c-‘0‘;
return x;
}
namespace output {
char s[2500000];
char *t=s;
inline void put(int x){
if(x==0)*t++=‘0‘;
else{
static int a[5];
int n=0;
for(;x>0;x/=10)a[++n]=x%10;
while(n>0)*t++=‘0‘+a[n--];
}
*t++=‘ ‘;
}
inline void flush(){fwrite(s,1,t-s,stdout);}
}
using output::put;
int main(){
int n,m,i,j;
n=rd();
for(i=1;i<=n;i++)s[i]=rd();
s[n+1]=1001;
m=rd();
for(i=n+2;i<=n+m+1;i++)s[i]=rd();
s[n+m+2]=1001;
suf(n+m+2);
i=1;
j=n+2;
while(i<=n&&j<=n+m+1){
if(rk[i]<rk[j]){
put(s[i]);
i++;
}else{
put(s[j]);
j++;
}
}
for(;i<=n;i++)put(s[i]);
for(;j<=n+m+1;j++)put(s[j]);
*(output::t)=‘\n‘;
output::flush();
}