又被机房神犇肉丝哥哥和glory踩爆了
首先这个答案的输出方式有点套路,当前的答案=上一个答案+每一个后缀的f值=上一个答案+上一次算的每个后缀的f值+当前每个后缀的深度
这个题意给了个根深度为-1有点诡异,考虑它的现实意义是这个后缀在前面出现了几次,这些后缀的深度和就是前面有多少子串和后缀是能匹配的
考虑用SAM把fail树搞出来,那么对于加入一个后缀,就是这个叶子到根的路径的点+1,询问也是问当前点到根的和
拿个LCT维护一下跑路
离线树剖好像更好写 肉丝早就跑路了
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int _=1e2; const int maxn=1e5+_; const LL mod=1e9+7; struct node { int f,son[2]; LL s,c;//子树内子串总出现数 子树内子串数 LL tot,num;//管理子串数,节点出现次数 LL lazy; }tr[2*maxn]; void update(int now) { int lc=tr[now].son[0],rc=tr[now].son[1]; tr[now].c=tr[lc].c+tr[rc].c+tr[now].tot; tr[now].s=(tr[lc].s+tr[rc].s+tr[now].tot*tr[now].num+(tr[now].c-tr[now].tot)*tr[now].lazy)%mod; } void pushdown(int now) { int lc=tr[now].son[0],rc=tr[now].son[1]; if(lc!=0)tr[lc].num+=tr[now].lazy,tr[lc].lazy+=tr[now].lazy,update(lc); if(rc!=0)tr[rc].num+=tr[now].lazy,tr[rc].lazy+=tr[now].lazy,update(rc); tr[now].lazy=0; } void rotate(int x,int w) { int f=tr[x].f,ff=tr[f].f; int R,r; R=f,r=tr[x].son[w]; tr[R].son[1-w]=r; if(r!=0)tr[r].f=R; R=ff,r=x; if(tr[ff].son[0]==f)tr[R].son[0]=r; else if(tr[ff].son[1]==f)tr[R].son[1]=r; tr[r].f=R; R=x,r=f; tr[R].son[w]=r; tr[r].f=R; update(f); update(x); } bool isroot(int x,int rt) { int f=tr[x].f; if(f==rt|| tr[f].son[0]!=x&&tr[f].son[1]!=x )return true; else return false; } int tt,tmp[2*maxn]; void splay(int x,int rt) { int i=x; tt=0; while(!isroot(i,rt)) tmp[++tt]=i,i=tr[i].f; tmp[++tt]=i; while(tt>0) { if(tr[tmp[tt]].lazy!=0)pushdown(tmp[tt]); tt--; } while(!isroot(x,rt)) { int f=tr[x].f,ff=tr[f].f; if(isroot(f,rt)) { if(tr[f].son[0]==x)rotate(x,1); else rotate(x,0); } else { if(tr[ff].son[0]==f&&tr[f].son[0]==x)rotate(f,1),rotate(x,1); else if(tr[ff].son[1]==f&&tr[f].son[1]==x)rotate(f,0),rotate(x,0); else if(tr[ff].son[0]==f&&tr[f].son[1]==x)rotate(x,0),rotate(x,1); else if(tr[ff].son[1]==f&&tr[f].son[0]==x)rotate(x,1),rotate(x,0); } } } void access(int x) { int y=0; while(x!=0) { splay(x,0); tr[x].son[1]=y; if(y!=0)tr[y].f=x; update(x); y=x;x=tr[y].f; } } void gotop(int x){access(x),splay(x,0);} void Link(int x,int y){gotop(x),tr[x].f=y;} void Cut(int x) { gotop(x); int lc=tr[x].son[0]; tr[lc].f=0;tr[x].son[0]=0; update(x); } void add(int x) { gotop(x); tr[x].num++;tr[x].lazy++; update(x); } LL getsum(int x){gotop(x);return tr[x].s;} void change(int x,LL tot) { gotop(x); tr[x].tot=tot; update(x); } struct SAM { int w[30],dep,fail; }ch[2*maxn];int cnt,last; int gettot(int x){return ch[x].dep-ch[ch[x].fail].dep;} LL insert(int k,int x) { int now=++cnt,pre=last; LL ret=0; ch[now].dep=k; while(pre!=0&&ch[pre].w[x]==0)ch[pre].w[x]=now,pre=ch[pre].fail; if(pre==0) { ch[now].fail=1; change(now,gettot(now)); Link(now,ch[now].fail); add(now); } else { int nxt=ch[pre].w[x]; if(ch[nxt].dep==ch[pre].dep+1) { ch[now].fail=nxt; change(now,gettot(now)); Link(now,ch[now].fail); ret=getsum(now); add(now); } else { int nnxt=++cnt; ch[nnxt]=ch[nxt]; ch[nnxt].dep=ch[pre].dep+1; ch[nxt].fail=nnxt; change(nxt,gettot(nxt)); tr[nnxt].num=tr[nxt].num;change(nnxt,gettot(nnxt)); Cut(nxt); Link(nnxt,ch[nnxt].fail); Link(nxt,ch[nxt].fail); //.......先处理nnxt和nxt....... ch[now].fail=nnxt; change(now,gettot(now)); Link(now,ch[now].fail); ret=getsum(now); add(now); //........再搞nnxt和now....... while(pre!=0&&ch[pre].w[x]==nxt)ch[pre].w[x]=nnxt,pre=ch[pre].fail; } } last=now; return ret; } char ss[maxn]; int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); int n; scanf("%d%s",&n,ss+1); LL ans=0,sum=0; cnt=last=1; for(int i=1;i<=n;i++) { sum=(sum+insert(i,ss[i]-'a'+1))%mod; ans=(ans+sum)%mod; printf("%lld ",ans); } return 0; }