Description
对于一个字符串|S|,我们定义fail[i],表示最大的x使得S[1…x]=S[i-x+1…i],满足(x<i)
显然对于一个字符串,如果我们将每个0<=i<=|S|看成一个结点,除了i=0以外i向fail[i]连边,这是一颗树的形状,根是0
我们定义这棵树是G(S),设f(S)是G(S)中除了0号点以外所有点的深度之和,其中0号点的深度为-1
定义key(S)等于S的所有非空子串S’的f(S’)之和
给定一个字符串S,现在你要实现以下几种操作:
1.在S最后面加一个字符
2.询问key(S)
善良的出题人不希望你的答案比long long大,所以你需要将答案对1e9+7取模
Input
第一行一个正整数Q
Q<=10^5
第二行一个长度为Q的字符串S
Output
输出Q行,第i行表示前i个字符组成的字符串的答案
Sample Input
5
abaab
Sample Output
0
0
1
4
9
题解
字符串算法题都是一个套路qwq??
我们思考对于一个串,怎么算答案
对于这个串的某两个相同子串,设较后的那个子串的结束位置是 u u u
那么这两个子串能给答案贡献 n − u + 1 n-u+1 n−u+1
同时再注意一个东西,我们把串从 i + 1 i+1 i+1缩小到 i i i的规模时,减小的其实就是相同的子串的数量
也就是把上面那个东西的贡献看成 1 1 1
所以,如果我们能够维护这个东西,即维护两种不同贡献的相同子串数
那么就可以从最后一个答案递推回第一个答案
我们离线把这个串的SAM和parent树建出来
考虑当前最后一个位置在parent树上的状态是什么,设他为 x x x
那么对于他的某个祖先 y y y,对于 y y y的另外一棵子树中的某个状态 z z z,显然, x x x与 z z z的最长公共后缀就是 m x ( y ) mx(y) mx(y),那么这两个状态能提供的相同子串就是 m x ( y ) mx(y) mx(y)
可知贡献都在一条链上,考虑树剖维护子树信息
每个点维护其轻儿子子树中 r i g h t right right集合的大小与其的 m x ( i ) mx(i) mx(i)的乘积,遇到重链的时候直接计算,轻链的时候暴力计算轻链父亲的贡献,那么复杂度就是 n l o g 2 n nlog^2n nlog2n的
删除一个点时只需要删除他到根上的轻链的父亲的贡献,这个也可以做到
那么一个点的贡献就可以计算出来了,又因为每次都是删除最后一个位置,所以不需要考虑系数
计算最后一个答案同理,按上面的过程模拟即可
复杂度 n l o g 2 n nlog^2n nlog2n
SAM和parent树真是个好玩的东西啊
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define lc now<<1
#define rc now<<1|1
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int stack[20];
inline void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(!x){putchar('0');return;}
int top=0;
while(x)stack[++top]=x%10,x/=10;
while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=200005;
const int mod=1e9+7;
char ch[MAXN];
struct SAM{int son[27],dep,parent;}tr[MAXN];int root,cnt,lst;
void add(int x)
{
int np=++cnt,p=lst;
tr[np].dep=tr[p].dep+1;
while(p&&!tr[p].son[x])tr[p].son[x]=np,p=tr[p].parent;
if(!p)tr[np].parent=root;
else
{
int q=tr[p].son[x];
if(tr[q].dep==tr[p].dep+1)tr[np].parent=q;
else
{
int nq=++cnt;tr[nq]=tr[q];
tr[nq].dep=tr[p].dep+1;
tr[q].parent=tr[np].parent=nq;
while(p&&tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent;
}
}
lst=np;
}
struct edge{int x,y,next;}a[2*MAXN];int len,last[MAXN];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
int son[MAXN],tot[MAXN],dep[MAXN],fa[MAXN],top[MAXN],ys[MAXN],n,m,z;
int in[MAXN],ot[MAXN],dfn;
void pre_tree_node(int x)
{
in[x]=++dfn;tot[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
fa[y]=x;dep[y]=dep[x]+1;
pre_tree_node(y);
if(tot[y]>tot[son[x]])son[x]=y;
tot[x]+=tot[y];
}
ot[x]=dfn;
}
int ri[MAXN];
void pre_tree_edge(int x,int tp)
{
ys[x]=++z;top[x]=tp;
if(son[x])pre_tree_edge(son[x],son[x]);
for(int k=last[x];k;k=a[k].next)
if(a[k].y!=son[x])pre_tree_edge(a[k].y,a[k].y);
}
void ad(int &x,int y){x+=y;if(x>=mod)x-=mod;}
void dl(int &x,int y){x-=y;if(x<0)x+=mod;}
int CA1[MAXN],CA2[MAXN],le[MAXN];
struct segtree
{
int cal[MAXN*4],lazy[MAXN*4],sum[MAXN*4];//这一段的mx-mn值
void buildtree1(int now,int l,int r)
{
if(l==r){sum[now]=CA2[l];return ;}
int mid=(l+r)/2;
buildtree1(lc,l,mid);buildtree1(rc,mid+1,r);
}
void buildtree(int now,int l,int r)
{
lazy[now]=0;
if(l==r){cal[now]=CA1[l];sum[now]=CA2[l];return ;}
int mid=(l+r)/2;
buildtree(lc,l,mid);buildtree(rc,mid+1,r);
}
void down(int now)
{
if(!lazy[now])return ;
ad(cal[lc],1LL*lazy[now]*sum[lc]%mod);ad(cal[rc],1LL*lazy[now]*sum[rc]%mod);
ad(lazy[lc],lazy[now]);ad(lazy[rc],lazy[now]);
lazy[now]=0;
}
void modify(int now,int l,int r,int ql,int qr,int c)
{
if(l==ql&&r==qr){ad(cal[now],1LL*c*sum[now]%mod);ad(lazy[now],c);return ;}
int mid=(l+r)/2;down(now);
if(qr<=mid)modify(lc,l,mid,ql,qr,c);
else if(mid+1<=ql)modify(rc,mid+1,r,ql,qr,c);
else modify(lc,l,mid,ql,mid,c),modify(rc,mid+1,r,mid+1,qr,c);
cal[now]=(cal[lc]+cal[rc])%mod;
}
int qry(int now,int l,int r,int ql,int qr)
{
if(l==ql&&r==qr)return cal[now];
int mid=(l+r)/2;down(now);
if(qr<=mid)return qry(lc,l,mid,ql,qr);
else if(mid+1<=ql)return qry(rc,mid+1,r,ql,qr);
else return (qry(lc,l,mid,ql,mid)+qry(rc,mid+1,r,mid+1,qr))%mod;
}
}seg;//维护的是除重儿子外的right的siz * 自身的mx-mn
int s[MAXN];
int lowbit(int x){return x&-x;}
void modify(int x,int c){for(;x<=cnt;x+=lowbit(x))ad(s[x],c);}
int qry(int x){int ret=0;for(;x>=1;x-=lowbit(x))ad(ret,s[x]);return ret;}
int getcal(int u,int l,int r)//得到u子树中除了l~r一段的right的siz
{
if(!l)return qry(ot[u])-qry(in[u]-1);
return (qry(ot[u])-qry(in[u]-1))-(qry(r)-qry(l-1));
}
int ans1,ans2,id[MAXN];//答案 后面一个串只算一次的答案
int getsum(int x)
{
int tx=top[x],re=0,lst=0;
while(tx!=1)
{
if(x!=tx)ad(re,seg.qry(1,1,cnt,ys[tx],ys[fa[x]]));
if(lst)
{
int sz=getcal(x,in[lst],ot[lst]);
ad(re,1LL*le[x]*sz%mod);
}
lst=tx;x=fa[tx];tx=top[x];
}
if(x!=tx)ad(re,seg.qry(1,1,cnt,ys[tx],ys[fa[x]]));
if(lst)
{
int sz=getcal(x,in[lst],ot[lst]);
ad(re,1LL*le[x]*sz%mod);
}
return re;
}
void mdall(int x,int c)
{
int tx=top[x];
while(tx!=1)
{
seg.modify(1,1,cnt,ys[x],ys[x],c);
x=fa[tx];tx=top[x];
}
seg.modify(1,1,cnt,ys[x],ys[x],c);
}
int answer[MAXN];
int main()
{
n=read();root=cnt=lst=1;
scanf("%s",ch+1);
for(int i=1;i<=n;i++)add(ch[i]-'a'+1);
for(int i=1;i<=cnt;i++)if(tr[i].parent)ins(tr[i].parent,i);
pre_tree_node(1);
pre_tree_edge(1,1);
for(int i=1;i<=cnt;i++)le[i]=tr[i].dep;
for(int i=1,p=root;i<=n;i++)
{
p=tr[p].son[ch[i]-'a'+1];
ri[p]++;id[i]=p;
}
for(int i=1;i<=cnt;i++)
{
int u=getcal(i,in[son[i]],ot[son[i]]);
CA2[ys[i]]=tr[i].dep-tr[tr[i].parent].dep;
CA1[ys[i]]=1LL*u*CA2[ys[i]]%mod;
}
seg.buildtree1(1,1,cnt);
for(int i=1;i<=n;i++)
{
int u=getsum(id[i]);
ad(ans2,u);ad(ans1,1LL*u*(n-i+1)%mod);
mdall(id[i],1);
modify(in[id[i]],1);
}
seg.buildtree(1,1,cnt);
for(int i=n;i>=1;i--)
{
answer[i]=ans1;
modify(in[id[i]],-1);
dl(ans1,ans2);
int u=getsum(id[i]);
dl(ans2,u);
mdall(id[i],-1);
}
for(int i=1;i<=n;i++)pr2(answer[i]);
return 0;
}