Luogu5334 [JSOI2019]节日庆典

Luogu5334 [JSOI2019]节日庆典

exkmp

本题的突破口在于,对于一个位置\(i\),从\(i\)以后可能成为最优解的后缀数量极少。

容易发现,当我们处理到位置\(k\)时,对于此时的两个后缀\(i,j(i<j)\),如果\(T_i,T_j\)均有可能在\(\ge k\)的位置成为最优解,那么一定满足\(\operatorname{lcp}(T_i,T_j) \ge k-j+1\)

但上面的限制仍然不够,对于长度相邻的两个后缀\(i,j(i<j)\),我们可以证明:\(k-j+1<j-i\)

利用反证法,假设\(k-j+1 \ge j-i\)

\(S_k=ABBC,T_i=BBCA,T_j=BCAB\)\(C\)\(B\)的前缀。

\(T_{2j-i}=CABB\)

我们先在\(k\)的情况下观察:

\(1.\)\(BCA \le CAB\)时,\(T_i \le T_j\)

\(2.\)\(BCA > CAB\)时,\(T_{2j-i}>T_j\)

因此在\(k\)处,\(j\)不可能是最优解。

那么我们扩展到\(k'>k\)处。

\(S_{k'}=ABBCD,T'_i=BBCDA,T'_j=BCDAB,T'_{2j-i}=CDABB\)

同理:

\(1.\)\(BCDA \le CDAB\)时,\(T'_i \le T'_j\)

\(2.\)\(BCDA > CDAB\)时,\(T'_{2j-i}>T'_j\)

因此\(j\)不可能再成为最优解了,可以直接剔除。

\(k-j+1<j-i\),容易得到满足条件的后缀数量为\(\log k\)

那么我们每次操作完成之后,直接暴力重构集合,利用exkmp可以\(O(1)\)比较两个后缀。

时间复杂度:\(O(n \log n)\)

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define il inline
#define rint register int
#define N 3000005
using namespace std;
int n,c,z[N],q[35];
bool vis[N];
char s[N];
il void print(int x)
{
    if (x>9)
        print(x/10);
    putchar(x%10+'0');
}
il int cmn(int x,int y)
{
    return (x<y)?x:y;
}
il void fil(int k)
{
    int c2(0);
    for (rint i=c-1;i;--i)
    {
        int wz(q[i]+(k-q[i+1]+1)-1);
        if (s[wz]<s[k] || s[wz]==s[k] && k-q[i+1]+1>=q[i+1]-q[i])
            vis[q[i+1]]=true;
        if (s[wz]>s[k])
            q[i]=q[i+1];
    }
    for (rint i=1;i<=c;++i)
        if (!vis[q[i]])
            q[++c2]=q[i]; else
            vis[q[i]]=false;
    swap(c,c2);
}
il void ckmin(int &i,int j,int k)
{
    if (z[i+(k-j+1)]>=j-i)
    {
        if (z[j-i+1]>=i-1)
            return;
        if (s[j-i+1+z[j-i+1]]<s[z[j-i+1]+1])
            i=j;
        return;
    }
    if (s[z[i+(k-j+1)]+1]<s[i+(k-j+1)+z[i+(k-j+1)]])
        i=j;
}
il int calc(int k)
{
    int ans=q[1];
    for (rint i=2;i<=c;++i)
        ckmin(ans,q[i],k);
    return ans;
}
int main()
{
    scanf("%s",s+1);
    z[1]=n=strlen(s+1);
    int l(1),r(0);
    for (rint i=2;i<=n;++i)
    {
        if (i<=r)
            z[i]=cmn(r-i+1,z[i-l+1]);
        while (i+z[i]<=n && s[i+z[i]]==s[z[i]+1])
            ++z[i];
        if (i+z[i]-1>r)
            l=i,r=i+z[i]-1;
    }
    for (rint i=1;i<=n;++i)
    {
        q[++c]=i;
        fil(i);
        print(calc(i)),putchar(' ');
    }
    putchar('\n');
    return 0;
}
posted @ 2021-02-28 11:25  GK0328  阅读(65)  评论(0编辑  收藏  举报