[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]; } }