• [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];
        }
    }
    
  • 相关阅读:
    HTML5 localStorage and sessionStorage
    WebViewJavascriptBridge-Obj-C和JavaScript互通消息的桥梁
    js控制手机号码中间用星号代替
    无法删除登录名 '***',因为该用户当前正处于登录状态。 (Microsoft SQL Server,错误: 15434)
    在QT中添加LIB的方法
    Android颜色代码
    判断ubuntu是32位还是64位
    Android应用市场App发布
    ubuntu目录结构(转)
    QT调用CHM方法
  • 原文地址:https://www.cnblogs.com/mollnn/p/11793279.html
Copyright © 2020-2023  润新知