[BZOJ4310]跳蚤 后缀数组+线段树

一个log的题被我强行写成了两个log
诶再怎么样过了就是了

构建SA,然后二分答案,贪心判断至少要剪断多少次才能使得答案成为最大的

/**************************************************************
    Problem: 4310
    User: di4CoveRy
    Language: C++
    Result: Accepted
    Time:12576 ms
    Memory:10084 kb
****************************************************************/

#include <bits/stdc++.h>
#define N 200050
#define INF (1<<30)
#define xx first
#define yy second
#define MP(x,y) make_pair(x,y)
using namespace std; 
typedef long long LL;
typedef pair<int,int> pii;
int sa[N],rank[N],nsa[N],nrank[N],height[N],n,k;
LL sum[N];
char s[N];
void _sort(int j) {
    memset(sum,0,sizeof(sum));
    for (int i=1;i<=n;i++) sum[ rank[i+j] ]++;
    for (int i=1;i<=n;i++) sum[i] += sum[i-1];
    for (int i=n;i>=1;i--) nsa[ sum[rank[i+j]]-- ] = i;

    memset(sum,0,sizeof(sum));
    for (int i=1;i<=n;i++) sum[ rank[i] ]++;
    for (int i=1;i<=n;i++) sum[i] += sum[i-1];
    for (int i=n;i>=1;i--) sa[ sum[rank[ nsa[i] ]]-- ] = nsa[i];
}

void _sa() {
    memset(sum,0,sizeof(sum));

    for (int i=1;i<=n;i++) rank[i] = s[i];
    for (int i=1;i<=n;i++) sum[ rank[i] ]++;
    for (int i=1;i<=255;i++) sum[i] += sum[i-1];
    for (int i=n;i>=1;i--) 
        sa[ sum[rank[i]]-- ] = i;

    nrank[ sa[1] ] = 1;
    for (int i=2,p=1;i<=n;i++) 
        nrank[ sa[i] ] = rank[ sa[i] ] == rank[ sa[i-1] ] ? p : ++p;
    for (int i=1;i<=n;i++) rank[i] = nrank[i];

    for (int j=1;j<=n;j<<=1) {
        _sort(j);
        nrank[ sa[1] ] = 1;
        for (int i=2,p=1;i<=n;i++) {
            if (rank[ sa[i] ] != rank[ sa[i-1] ] || rank[ sa[i]+j ] != rank[ sa[i-1]+j ]) p++;
            nrank[ sa[i] ] = p;
        }
        for (int i=1;i<=n;i++) rank[i] = nrank[i];
    }

    return ;
}

void _hi() {
        for (int i=1,j=0;i<=n;i++) {
                if (rank[i]==1) continue;
                for (;s[i+j]==s[sa[rank[i]-1]+j];j++);
                height[rank[i]]=j;
                if (j) j--;
        }
        return ;
}

int tr[4*N],ll,rr;
#define tmd ( (l + r) >> 1 )
#define ls l,tmd,t<<1
#define rs tmd+1,r,t<<1^1
int build(int l,int r,int t) {
    return l == r ? tr[t] = height[l] : tr[t] = min( build(ls) , build(rs) );
}

int query(int l,int r,int t) {
    if (l > rr || r < ll) return INF;
    if (l >= ll && r <= rr) return tr[t];
    int p1 = query(ls);
    int p2 = query(rs);
    return min(p1,p2);
}

pii get(LL x) {
    for (int i=1;i<=n;i++) 
        if (sum[ sa[i] ] >= x) {
            x = x - sum[ sa[i-1] ] + height[i];
            return MP(sa[i],x);
        }
    assert(0); return MP(0,0);
}

int lcp(int p1,int p2) {
    p1 = rank[p1] , p2 = rank[p2];
    if (p1 > p2) swap(p1,p2);
    ll = p1 + 1 , rr = p2;
    return query(1,n,1);
}

int go(pii A) {
    int ret = 1 , last = n;
    for (int i=n;i>=1;i--) {
        if (s[i] > s[A.xx]) return INF;
        int tmp = lcp(A.xx,i);
        if (A.xx == i && last-i+1 <= A.yy) continue; 
        if (tmp > A.yy) {
            last = i , ret++;
            continue;
        }
        if (tmp >= last-i+1) continue;
        if (s[i+tmp] <= s[A.xx+tmp]) continue;
        last = i , ret++;
    }
    return ret;
}

void _solve() {
    memset(sum,0,sizeof(sum));
    for (int i=1;i<=n;i++) {
        int len = n-sa[i]+1;
        sum[ sa[i] ] = sum[ sa[i-1] ] + len - height[i];
    }

    build(1,n,1);

    LL l = 1 , r = sum[ sa[n] ];
    while (l < r) {
        LL mid = (l + r) >> 1;
        pii cur = get(mid);
        if (go(cur) > k) l = mid+1; else r = mid;
    }
    pii ans = get(l);
    for (int i=ans.xx;i<=ans.xx+ans.yy-1;i++) printf("%c",s[i]); puts("");
}

int main() {
//  freopen("flea.in","r",stdin);
//  freopen("flea.out","w",stdout);
    scanf("%d",&k);
    scanf("%s",s+1); n = strlen(s+1);
    _sa(); _hi();
    _solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值