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;
}