[HihoCoder1413]Rikka with String
阿新 • • 發佈:2018-04-12
AR CP mat () 需要 成了 我們 sizeof -i
出現在這個區間裏那麽\(i\)這個狀態表示的所有長度大於等於一定值的子串就都會被砍斷。所以給這個區間集體加上前一個區間的長度,即\(\min(r_i-len_{fa_i},l_i)-(r_i-len_i+1)+1\)。
vjudge
題意
給你一個串,問你把每個位置的字符替換成#
後串中有多少本質不同的子串。
\(n\le 3*10^5\)
sol
首先可以計算出原串裏面有多少本質不同的子串。顯然就是\(\sum_{i=1}^{tot}len_i-len_{fa_i}\)。
然後考慮把\(i\)位置替換成了#
,那麽你從\(1..i\)中任選左端點,\(i..n\)中任選右端點,這樣的子串一定會是本質不同的。(一定含有字符#
,滿足長度不同或#
的出現位置不同)
所以對於位置\(i\),答案先加上\(i*(n-i+1)\)。
然後也可以再給答案加上原有的本質不同的子串數目,這樣我們就只需要求出:把\(i\)位置改成#
後有多少本質不同的子串不再出現了(被砍斷)。
考慮一個\(SAM\)中的狀態\(i\),設其最大長度\(len_i\),\(right\)集合中的最小最大元素分別為\(l_i,r_i\)。
對於區間\([r_i-len_i+1,\min(r_i-len_{fa_i},l_i)]\)(如果合法的話),我們會給這個區間加上一個首項是\(1\)公差也是\(1\)的等差數列。
因為這個狀態\(i\)本身是表示了\(len_i-len_{fa_i}+1\)個連續長度的子串,所以在這個區間內每向右移動一下就會有一個子串被砍斷,因此是一個公差為\(1\)的等差數列。
對於區間\([min(r_i-len_{fa_i},l_i)+1,l_i]\)(如果合法的話),一旦#
等差數列用二階差分維護一下就可以了,註意原一階差分(第二種情況就是一個一階差分)對應的二階差分。所以構出\(SAM\)後的復雜度可以做到線性。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 6e5+5 ;
int n,last=1,tot=1,tr[N][26],fa[N],len[N],l[N],r[N],t[N],a[N];
char s[N];long long sum,ans[N];
void extend(int c)
{
int v=last,u=++tot;last=u;
len[u]=len[v]+1;
while (v&&!tr[v][c]) tr[v][c]=u,v=fa[v];
if (!v) fa[u]=1;
else{
int x=tr[v][c];
if (len[x]==len[v]+1) fa[u]=x;
else{
int y=++tot;
memcpy(tr[y],tr[x],sizeof(tr[y]));
fa[y]=fa[x];fa[x]=fa[u]=y;len[y]=len[v]+1;
while (v&&tr[v][c]==x) tr[v][c]=y,v=fa[v];
}
}
}
int main()
{
scanf("%d",&n);scanf("%s",s+1);
memset(l,63,sizeof(l));
for (int i=1;i<=n;++i) extend(s[i]-'a'),l[last]=r[last]=i;
for (int i=1;i<=tot;++i) ++t[len[i]];
for (int i=1;i<=tot;++i) t[i]+=t[i-1];
for (int i=1;i<=tot;++i) a[t[len[i]]--]=i;
for (int i=tot;i;--i)
{
l[fa[a[i]]]=min(l[fa[a[i]]],l[a[i]]);
r[fa[a[i]]]=max(r[fa[a[i]]],r[a[i]]);
sum+=len[i]-len[fa[i]];
}
for (int i=2;i<=tot;++i)
{
int L=r[i]-len[i]+1,R=min(r[i]-len[fa[i]],l[i]),Len=R-L+1;
if (L<=R) ans[L]+=1,ans[R+1]-=Len+1,ans[R+2]+=Len;
L=R+1;R=l[i];
if (L<=R) ans[L]+=Len,ans[L+1]-=Len,ans[R+1]-=Len,ans[R+2]+=Len;
}
for (int i=1;i<=n;++i) ans[i]+=ans[i-1];
for (int i=1;i<=n;++i) ans[i]+=ans[i-1];
for (int i=1;i<=n;++i) printf("%lld ",1ll*i*(n-i+1)+sum-ans[i]);
puts("");return 0;
}
[HihoCoder1413]Rikka with String