传送门
解析:
想法是算出每次新加一个字符的贡献增量,也就是差分数组。
我们发现每次新加入一个字符,会产生 l e n len len个新的后缀, l e n len len是加入前字符串的长度。
而在这些新的后缀的子串中,不是后缀的所有串的贡献其实就是之前作为过后缀的所有串的贡献,所以我们只需要算增量的差分数组,也就是二阶差分数组。
所以我们需要考虑每一个后缀在之前的所有位置出现了多少次。
显然我们直接提取最后插入节点到SAM根节点的fail链,(直接上LCT)。
我们发现每个节点表示的字符串出现次数实际上就是right集合大小,而每个节点代表的字符串个数就是 u − > l e n − u − > f a − > l e n u->len-u->fa->len u−>len−u−>fa−>len,直接上LCT维护答案就行了。
然后直接二阶差分回去就行了。
(实际上51nod数据水到家了,现在榜上rank1还是个 O ( n 2 ) O(n^2) O(n2)的暴力跳fail树,这位老哥还在讨论区怼出题人说数据过于水来着。。。)
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
using std::cout;
using std::cerr;
using std::cin;
cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(int a,int b){return (ll)a*b%mod;}
cs int N=2e5+5;
namespace LCT{
static cs int N=::N<<1;
int son[N][2],fa[N];
int len[N],sumlen[N],val[N],r[N],add[N];
inline bool isroot(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
inline bool which(int u){return son[fa[u]][1]==u;}
inline void pushup(int u){
sumlen[u]=len[u];
val[u]=mul(len[u],r[u]);
if(son[u][0]){
sumlen[u]=::add(sumlen[u],sumlen[son[u][0]]);
val[u]=::add(val[u],val[son[u][0]]);
}
if(son[u][1]){
sumlen[u]=::add(sumlen[u],sumlen[son[u][1]]);
val[u]=::add(val[u],val[son[u][1]]);
}
}
inline void pushadd(int u,int w){
add[u]=::add(add[u],w);
r[u]=::add(r[u],w);
val[u]=::add(val[u],mul(w,sumlen[u]));
}
inline void pushdown(int u){
if(add[u]){
if(son[u][0])pushadd(son[u][0],add[u]);
if(son[u][1])pushadd(son[u][1],add[u]);
add[u]=0;
}
}
inline void Rotate(int u){
int Fa=fa[u],FA=fa[Fa];
bool pos=which(u);
if(FA&&!isroot(Fa))son[FA][which(Fa)]=u;
son[Fa][pos]=son[u][!pos];
if(son[u][!pos])fa[son[u][!pos]]=Fa;
son[u][!pos]=Fa;
fa[Fa]=u,fa[u]=FA;
pushup(Fa),pushup(u);
}
inline void Splay(int u){
static int q[N],qn;
q[qn=1]=u;
for(int re Fa=u;!isroot(Fa);Fa=fa[Fa])q[++qn]=fa[Fa];
while(qn)pushdown(q[qn--]);
for(int re Fa=fa[u];!isroot(u);Rotate(u),Fa=fa[u])
if(!isroot(Fa))Rotate(which(Fa)==which(u)?Fa:u);
}
inline void access(int u){
for(int re ch=0;u;u=fa[ch=u]){
Splay(u);son[u][1]=ch;pushup(u);
}
}
inline void select(int u){access(u);Splay(u);}
inline void link(int u,int v){
select(v);
fa[u]=v;
}
inline void cut(int u,int v){
select(u);
Splay(v);
son[v][1]=fa[u]=0;
pushup(v);
}
}
namespace SAM{
static cs int N=::N<<1;
int son[N][26],fa[N],len[N],r[N];
int last=1,now=1;
inline void init_LCT(int cur){
LCT::len[cur]=len[cur]-len[fa[cur]];
LCT::r[cur]=r[cur];
LCT::pushup(cur);
}
inline int push_back(char c){
c-='a';
int cur=++now,p=last;
len[cur]=len[last]+1,r[cur]=1;
for(;p&&!son[p][c];p=fa[p])son[p][c]=cur;
if(!p)fa[cur]=1,init_LCT(cur),LCT::link(cur,1);
else if(len[son[p][c]]==len[p]+1)fa[cur]=son[p][c],init_LCT(cur),LCT::link(cur,fa[cur]);
else {
int clone=++now,q=son[p][c];
len[clone]=len[p]+1;
memcpy(son[clone],son[q],sizeof son[q]);
fa[clone]=fa[q];
LCT::select(q);
r[clone]=r[q]=LCT::r[q];
init_LCT(clone);
LCT::link(clone,fa[q]);
LCT::cut(q,fa[q]);
fa[q]=fa[cur]=clone;
init_LCT(q);
init_LCT(cur);
LCT::link(q,clone);
LCT::link(cur,clone);
for(;p&&son[p][c]==q;p=fa[p])son[p][c]=clone;
}
last=cur;
LCT::select(cur);
int now=LCT::son[cur][0];
int res=LCT::val[now];
LCT::pushadd(now,1);
return res;
}
}
int n;
char s[N];
int res,ans;
signed main(){
std::ios::sync_with_stdio(false);
cin>>n>>(s+1);
for(int re i=1;i<=n;++i){
res=add(res,SAM::push_back(s[i]));
cout<<(ans=add(ans,res))<<"\n";
}
return 0;
}