[BZOJ4310] 跳蚤

[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];
    }
}
原文地址:https://www.cnblogs.com/mollnn/p/11793279.html