【BZOJ4278】[ONTAK2015] Tasowanie(后缀数组+贪心)
大致题意: 给定两个数字串,让你对它们进行归并得到一个字典序最小的新串。
贪心
一看完题面首先想到无脑贪心,优先选小的数。
然后突然想到碰到两个一样的数该先选谁?于是无脑贪心就被\(Hack\)掉了。
所以,我们来考虑有脑贪心。
考虑先把两个串并起来后缀排序,则我们优先选择后缀排序中后缀排名较为靠前(即\(rk\)较小)的数即可。
至于正确性我想应该显然。
注意每个串最后要放一个大数(可以分别放上\(1001\)和\(1002\)),不然样例都过不了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
using namespace std;
int n,m,s[2*N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
class SuffixArray//后缀数组
{
private:
int n,SA[2*N+5],p[2*N+5],t[2*N+5];
I void Sort(CI S)
{
RI i;for(i=0;i<=S;++i) t[i]=0;for(i=1;i<=n;++i) ++t[rk[i]];
for(i=1;i<=S;++i) t[i]+=t[i-1];for(i=n;i;--i) SA[t[rk[p[i]]]--]=p[i];
}
public:
int rk[2*N+5];
I void GetSA(CI x,int *s)
{
RI i;for(n=x,i=1;i<=n;++i) rk[p[i]=i]=s[i];
RI k,t=0,S=1002;for(Sort(S),k=1;t^n;S=t,k<<=1)
{
for(t=0,i=1;i<=k;++i) p[++t]=n-k+i;
for(i=1;i<=n;++i) SA[i]>k&&(p[++t]=SA[i]-k);
for(Sort(S),i=1;i<=n;++i) p[i]=rk[i];
for(rk[SA[1]]=t=1,i=2;i<=n;++i)
rk[SA[i]]=(p[SA[i-1]]^p[SA[i]]||p[SA[i-1]+k]^p[SA[i]+k])?++t:t;
}
}
}S;
int main()
{
RI i,j;for(F.read(n),i=1;i<=n;++i) F.read(s[i]);s[n+1]=1001;
for(F.read(m),i=1;i<=m;++i) F.read(s[n+1+i]);s[n+m+2]=1002;S.GetSA(n+m+2,s);
i=1,j=1;W(i<=n&&j<=m) F.write(S.rk[i]<S.rk[n+1+j]?s[i++]:s[n+1+(j++)],' ');//归并
W(i<=n) F.write(s[i]," \n"[i==n]),++i;W(j<=m) F.write(s[n+1+j]," \n"[j==m]),++j;//处理剩余部分
return F.clear(),0;
}
待到再迷茫时回头望,所有脚印会发出光芒