题目本来是挺简单的,可惜我的常数太大,用了各种黑科技才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(); }