题意
考虑转化下\(f(S)\):
我们考虑\(S\)的\(fail\)树上的每一个点\(x\),如果\(x\)到\(0\)的路径上有\(cnt\)个点(除去\(x\)和\(0\)),那么就会对\(f(S)\)产生\(cnt\)的贡献,于是现在\(f(S)\)变为了每个点会对很多点产生\(1\)的贡献,最后求和。
我们知道每个点代表一个前缀,那么我们就可以转化成每个前缀的贡献若干个\(1\)。易知,如果一个前缀在\(S\)中出现了\(cnt\)次(除了它本身),那么就会产生\(cnt\)的贡献。
我们再来想\(key(S)\):
\(key(S)=\sum\limits_{S'\in S}f(S')\)。
我们统计每个子串的贡献,假如有两个子串\(S_{l1,r2},S_{l2,r2}(l1<l2)\)相等,那么就会产生\(n-r2\)的贡献。
但这样还是不好算,但是题中已经给我们提示了,我们是不断向后添加字符的,每次会新添加一个\(endpos\),算出增量即可。
每次的增量\(sum_i\):
\(sum_i=sum_{i-1}+calc(i)\)。
\(calc(i)\)表示从\(endpos_i\)(\(endpos\)集合中只包含\(i\)的点)到根(\(1\))的这条链上,所有的串都要贡献\(1\),我们求个和。
\(sum_{i-1}\)是上一次的增量,因为我们向后添加了一个字符,所以之前所有的串的贡献都要\(+1\)。
树剖/\(LCT\)维护即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
const int maxn=2e5+10;
const int mod=1e9+7;
int n,ans;
int endpos[maxn];
char s[maxn];
inline int read()
{
char c=getchar();int res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
struct SAM
{
int last,tot;
int fa[maxn],len[maxn];
int ch[maxn][26];
SAM(){last=tot=1;}
inline void add(int c)
{
int now=++tot;len[now]=len[last]+1;
int p=last;last=now;
while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
if(!p){fa[now]=1;return;}
int q=ch[p][c];
if(len[q]==len[p]+1)fa[now]=q;
else
{
int nowq=++tot;len[nowq]=len[p]+1;
memcpy(ch[nowq],ch[q],sizeof(ch[q]));
fa[nowq]=fa[q],fa[q]=fa[now]=nowq;
while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
}
}
}sam;
int cnt_edge;
int head[maxn];
struct edge{int to,nxt;}e[maxn<<1];
inline void add_edge(int u,int v)
{
e[++cnt_edge]=(edge){v,head[u]};
head[u]=cnt_edge;
}
int size[maxn],son[maxn],dep[maxn],pre[maxn];
void dfs1(int x,int fa)
{
size[x]=1;
dep[x]=dep[pre[x]=fa]+1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa)continue;
dfs1(y,x);size[x]+=size[y];
if(size[y]>size[son[x]])son[x]=y;
}
}
int tim;
int dfn[maxn],pos[maxn],top[maxn];
void dfs2(int x,int tp)
{
pos[dfn[x]=++tim]=x;
top[x]=tp;
if(son[x])dfs2(son[x],tp);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==son[x]||y==pre[x])continue;
dfs2(y,y);
}
}
struct Segment_tree
{
#define len(p) (seg[p].len)
#define sum(p) (seg[p].sum)
#define tag(p) (seg[p].tag)
int len,sum,tag;
}seg[maxn<<2];
void build(int p,int l,int r)
{
if(l==r){len(p)=sam.len[pos[l]]-sam.len[sam.fa[pos[l]]];return;}
int mid=(l+r)>>1;
build(ls(p),l,mid);build(rs(p),mid+1,r);
len(p)=add(len(ls(p)),len(rs(p)));
}
inline void down(int p)
{
if(!tag(p))return;
int k=tag(p);
sum(ls(p))=add(sum(ls(p)),1ll*k*len(ls(p))%mod);
sum(rs(p))=add(sum(rs(p)),1ll*k*len(rs(p))%mod);
tag(ls(p))=add(tag(ls(p)),k),tag(rs(p))=add(tag(rs(p)),k);
tag(p)=0;
}
void change(int p,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)
{
sum(p)=add(sum(p),len(p));
tag(p)++;
return;
}
down(p);
int mid=(l+r)>>1;
if(ql<=mid)change(ls(p),l,mid,ql,qr);
if(qr>mid)change(rs(p),mid+1,r,ql,qr);
sum(p)=add(sum(ls(p)),sum(rs(p)));
}
int query(int p,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)return sum(p);
down(p);
int mid=(l+r)>>1,res=0;
if(ql<=mid)res=add(res,query(ls(p),l,mid,ql,qr));
if(qr>mid)res=add(res,query(rs(p),mid+1,r,ql,qr));
return res;
}
inline void change(int x)
{
while(top[x]!=1)change(1,1,sam.tot,dfn[top[x]],dfn[x]),x=pre[top[x]];
change(1,1,sam.tot,dfn[1],dfn[x]);
}
inline int query(int x)
{
int res=0;
while(top[x]!=1)res=add(res,query(1,1,sam.tot,dfn[top[x]],dfn[x])),x=pre[top[x]];
res=add(res,query(1,1,sam.tot,dfn[1],dfn[x]));
return res;
}
int main()
{
n=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++)sam.add(s[i]-'a'),endpos[i]=sam.last;
for(int i=1;i<=sam.tot;i++)add_edge(sam.fa[i],i),add_edge(i,sam.fa[i]);
dfs1(1,0),dfs2(1,1);
build(1,1,sam.tot);
int sum=0;
for(int i=1;i<=n;i++)
{
sum=add(sum,query(endpos[i]));
change(endpos[i]);
ans=add(ans,sum);
printf("%d\n",ans);
}
return 0;
}