zoukankan      html  css  js  c++  java
  • BZOJ 4310 跳蚤

    首先我们知道我们要求的是使得最大值最小,显然是要二分的

    我们先对原串建出后缀自动机

    之后二分答案是第k小的字符串

    对于答案可行性的判定:

    我们注意到对于每一个区间,其字典序最大的子串一定是区间的某个后缀

    那么我们不妨从后往前扫,这样每次只会增加一个后缀

    我们只需要判断这个后缀是否比当前答案小就可以了

    如果比当前答案大,就划分出一组

    可以证明,这样分组是在满足条件的情况下分组最少的

    至于两个串比较大小,可以用哈希做到O(logn)

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    using namespace std;
     
    typedef unsigned long long LL;
    const int maxn=400010;
    const int x=13331;
    int k,n,clen;
    char s[maxn];
    char c[maxn];
    bool vis[maxn];
    LL dp[maxn];
    LL xp[maxn],h[maxn],H[maxn];
     
    int cnt=0,la=0;
    struct Node{
        int next[26];
        int len,link;
    }st[maxn];
    void init(){
        cnt=la=0;
        st[0].link=-1;
    }
    void add(int c){
        int cur=++cnt;
        st[cur].len=st[la].len+1;
        int p;
        for(p=la;p!=-1&&st[p].next[c]==0;p=st[p].link)st[p].next[c]=cur;
        if(p==-1)st[cur].link=0;
        else{
            int q=st[p].next[c];
            if(st[q].len==st[p].len+1)st[cur].link=q;
            else{
                int clone=++cnt;
                st[clone]=st[q];
                st[clone].len=st[p].len+1;
                for(;p!=-1&&st[p].next[c]==q;p=st[p].link)st[p].next[c]=clone;
                st[q].link=st[cur].link=clone;
            }
        }la=cur;
    }
    LL Go(int u){
        if(vis[u])return dp[u];
        vis[u]=true;dp[u]=1;
        for(int i=0;i<26;++i){
            if(st[u].next[i])dp[u]+=Go(st[u].next[i]);
        }return dp[u];
    }
    void Get_C(LL k){
        int now=0;
        while(1){
            for(int i=0;i<26;++i){
                if(st[now].next[i]){
                    int v=st[now].next[i];
                    if(k>dp[v])k-=dp[v];
                    else {c[++clen]=i+'a';now=v;break;}
                }
            }k--;
            if(k==0)return;
        }return;
    }
    LL Hash_C(int L,int R){return H[L]-H[R+1]*xp[R-L+1];}
    LL Hash_S(int L,int R){return h[L]-h[R+1]*xp[R-L+1];}
    bool cmp(int a,int b){
        if(c[1]<s[a])return false;
        else if(c[1]>s[a])return true;
        int L=a,R=min(L+clen-1,b);
        while(L<R){
            int mid=L+((R-L+1)>>1);
            if(Hash_C(1,mid-a+1)!=Hash_S(a,mid))R=mid-1;
            else L=mid;
        }L++;
        if(L>b)return true;
        else if(L-a+1>clen)return false;
        if(c[L-a+1]<s[L])return false;
        else if(c[L-a+1]>s[L])return true;
    }
    bool check(){
        int ans=0,p;
        for(int i=n;i;i=p){
            p=i;
            while(p&&cmp(p,i))p--;
            if(p==i)return false;
            ans++;
        }return ans<=k;
    }
     
    int main(){
        scanf("%d",&k);
        scanf("%s",s+1);n=strlen(s+1);
        init();
        for(int i=1;i<=n;++i)add(s[i]-'a');
        Go(0);xp[0]=1;h[n+1]=0;
        for(int i=1;i<=n;++i)xp[i]=xp[i-1]*x;
        for(int i=n;i>=1;--i)h[i]=h[i+1]*x+s[i]-'a';
        LL L=1,R=dp[0]-1;
        while(L<R){
            LL mid=(L+R)>>1;
            clen=0;Get_C(mid);
            H[clen+1]=0;
            for(int i=clen;i>=1;--i)H[i]=H[i+1]*x+c[i]-'a';
            if(check())R=mid;
            else L=mid+1;
        }
        clen=0;Get_C(R);
        for(int i=1;i<=clen;++i)printf("%c",c[i]);
        printf("
    ");return 0;
         
    }
    

      

  • 相关阅读:
    hihoCoder#1040 矩形判断
    hihoCoder#1038 01背包
    hihoCoder#1037 数字三角形
    hihoCoder#1120 小Hi小Ho的惊天大作战:扫雷·三
    hihoCoder#1119 小Hi小Ho的惊天大作战:扫雷·二
    Python核心编程读笔 3
    Python核心编程读笔 2
    EC读书笔记系列之12:条款22、23、24
    Linux程序设计 读笔2 Shell脚本
    Linux程序设计 读笔1
  • 原文地址:https://www.cnblogs.com/joyouth/p/5352813.html
Copyright © 2011-2022 走看看