[BZOJ4310] 跳蚤
Description
首先,他会把串分成不超过 (k) 个子串,然后对于每个子串 (S) ,他会从 (S) 的所有子串中选择字典序最大的那一个,并在选出来的 (k) 个子串中选择字典序最大的那一个。他称其为“魔力串”。现在他想找一个最优的分法让“魔力串”字典序最小。
Solution
我们将某个子串在所有子串(本质不同)中按照字典序的排名称作它的字典序排名。思考如何使用后缀数组快速求出一个子串的字典序排名。
假设我们要求 (s[l,r]) 这个子串的字典序排名,我们要在后缀排序中,从 (r[l]) 位置开始,向左找到第一个 (h[i]<r-l+1) 的位置,这个过程可以用二分 + ST表完成,那么 (ra[i]-(n-sa[i]+1)+(r-l+1)) 就是它的排名。
二分最终魔力串的字典序排名。我们倒着将整个串的字符一个个从前面插入,维护当前串的排名,如果当前串排名大于魔力串排名,那么这个当前串就危险了,我们要从它的第一个字符后面切开一刀。
这样我们只需要比较切的次数和 (k) 的关系,就可以调整二分边界。时间复杂度 (O(nlogn))
我可真是个奥比啊 输出个方案挂了十几发
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
int n,m=256,sa[N],y[N],u[N],v[N],o[N],r[N],h[N],T,ra[N],Log2[N],lim,k,tbuf[N];
char str[N];
int rc=0;
struct St {
int a[N][21];
void build(int *src,int n) {
for(int i=1;i<=n;i++) a[i][0]=src[i];
for(int i=1;i<=20;i++)
for(int j=1;j<=n-(1<<i)+1;j++)
a[j][i]=min(a[j][i-1],a[j+(1<<(i-1))][i-1]);
}
inline int query(int l,int r) {
int j=Log2[r-l+1];
return min(a[l][j],a[r-(1<<j)+1][j]);
}
} st;
void generate_rank() {
for(int i=1;i<=n;i++) {
ra[i]=ra[i-1]+(n-sa[i]+1)-h[i];
}
}
inline int bound(int l,int r,int v) {
while(r>l) {
int mid=(l+r)>>1;
if(st.query(mid+1,r)<v) l=mid+1;
else r=mid;;
}
return l;
}
inline int getrank(int L,int R) {
int i=bound(1,r[L],R-L+1);
return ra[i]-(n-sa[i]+1)+(R-L+1);
}
signed main(){ //freopen("inp.txt","r",stdin);
memset(str,0,sizeof str);
scanf("%lld%s",&k,str+1);
n=strlen(str+1);
for(int i=1;i<=n;i++) Log2[i]=log2(i);
for(int i=1;i<=n;i++) u[str[i]]++;
for(int i=1;i<=m;i++) u[i]+=u[i-1];
for(int i=n;i>=1;i--) sa[u[str[i]]--]=i;
r[sa[1]]=1;
for(int i=2;i<=n;i++) r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
for(int l=1;r[sa[n]]<n;l<<=1) {
memset(u,0,sizeof u);
memset(v,0,sizeof v);
memcpy(o,r,sizeof r);
for(int i=1;i<=n;i++) u[r[i]]++, v[r[i+l]]++;
for(int i=1;i<=n;i++) u[i]+=u[i-1], v[i]+=v[i-1];
for(int i=n;i>=1;i--) y[v[r[i+l]]--]=i;
for(int i=n;i>=1;i--) sa[u[r[y[i]]]--]=y[i];
r[sa[1]]=1;
for(int i=2;i<=n;i++) r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
}
{
int i,j,k=0;
for(int i=1;i<=n;h[r[i++]]=k)
for(k?k--:0,j=sa[r[i]-1];str[i+k]==str[j+k];k++);
}
st.build(h,n);
generate_rank();
lim=n*(n+1)/2;
for(int i=1;i<=n;i++) lim-=h[i];
int l=1,r=ra[n],al,aw;
while(r>l) {
int mid=(l+r)/2,se=n,cnt=0,tl,tw,tx=0;
for(int i=n;i>=1;--i) {
if(getrank(i,se)>mid) {
if(i==se) {
cnt=INT_MAX;
break;
}
++cnt;
se=i;
}
if(cnt>k) break;
}
++cnt;
if(cnt<=k) r=mid;
else l=mid+1;
}
ra[n+1]=INT_MAX;
for(int i=1;i<=n;i++) {
if(n-sa[i]+1 - h[i] >= l) {
for(int j=sa[i]; j<=sa[i]+h[i]+l-1; j++) printf("%c",str[j]);
return 0;
}
else l-=n-sa[i]+1-h[i];
}
}