题目大意:给出一个字符串,对于它的每个前缀S,求出S所有子串(不要求本质不同)的所有前缀在border树上的(深度-1)的和。例如
,对于字符串abab,它的第四个前缀S4=abab,有子串a,b,a,b,ab,ba,ab,aba,bab,abab,他们的所有前缀在border树上的(深度-1)的和分别是0,0,0,0,0,0,0,1,1,2,因此答案是4。
对于一个串S,它的所有前缀在border树上的(深度-1)的和,等于它的所有前缀在S中出现次数减去|S|。因为考虑任意一个前缀和这个前缀在后面某个位置出现的位置,必然存在一个border让其产生1的贡献。
对于一个串S,它的所有子串的所有前缀在border树上的(深度-1)的和,等于对于所有本质不同的字符串S,$\tbinom{cnt_S}{2}$之和,其中$cnt_S$表示S的出现次数。因为考虑任意两个位置不同的相同子串,必然存在一个border让其产生1的贡献。
因此,暴力的方法是对于每一个前缀,建立后缀树,并在每个节点上统计$\tbinom{sum_u}{2}*(len_{u}-len_{fa})$的和。
考虑每次加一个字符的过程,相当于是选择一条从一个点到根的路径,将路径上所有点的sum加一,而答案仅仅是一个关于x的二次函数。我们对于每个点维护$f(x)=ax^2+bx+c$,并规定最终的答案为$x=0$时$f(x)$的值。显然路径加满足结合律,用树剖或lct即可。
1 #include<bits/stdc++.h> 2 #define mod 1000000007 3 #define G2 500000004 4 using namespace std; 5 typedef long long int ll; 6 const int maxn=3E5+5; 7 int n; 8 string str; 9 ll ans; 10 namespace SAM 11 { 12 struct node 13 { 14 int fa,len,ch[26]; 15 }t[maxn*2]; 16 int tot=1,last=1; 17 int where[maxn],sum[maxn],minPos[maxn]; 18 inline void add(int x,int id) 19 { 20 int now=++tot,u=last; 21 t[now].len=t[u].len+1; 22 last=tot; 23 where[id]=now; 24 minPos[now]=id; 25 for(;u&&!t[u].ch[x];u=t[u].fa) 26 t[u].ch[x]=now; 27 if(!u) 28 t[now].fa=1; 29 else 30 { 31 int v=t[u].ch[x]; 32 if(t[v].len==t[u].len+1) 33 t[now].fa=v; 34 else 35 { 36 int w=++tot; 37 t[w]=t[v]; 38 t[w].len=t[u].len+1; 39 t[now].fa=t[v].fa=w; 40 for(;u&&t[u].ch[x]==v;u=t[u].fa) 41 t[u].ch[x]=w; 42 } 43 } 44 } 45 } 46 struct info 47 { 48 ll a,b,c; 49 info(ll A=0,ll B=0,ll C=0):a(A),b(B),c(C){}; 50 info operator*(const int&d) 51 { 52 info A; 53 A.a=a; 54 A.b=(b+a*d*2%mod)%mod; 55 A.c=((a*d+b)%mod*d%mod+c)%mod; 56 return A; 57 } 58 info operator+(const info&A) 59 { 60 info B; 61 B.a=(a+A.a)%mod; 62 B.b=(b+A.b)%mod; 63 B.c=(c+A.c)%mod; 64 return B; 65 } 66 }; 67 namespace LCT 68 { 69 int fa[maxn],son[maxn][2]; 70 info ans[maxn],val[maxn]; 71 int tag[maxn]; 72 inline bool notroot(int x) 73 { 74 return son[fa[x]][0]==x||son[fa[x]][1]==x; 75 } 76 inline void pushup(int x) 77 { 78 ans[x]=ans[son[x][0]]+ans[son[x][1]]+val[x]; 79 } 80 inline void rotate(int x) 81 { 82 int y=fa[x]; 83 int c=son[y][0]==x; 84 fa[x]=fa[y]; 85 son[y][!c]=son[x][c]; 86 if(son[x][c]) 87 fa[son[x][c]]=y; 88 if(notroot(y)) 89 son[fa[y]][son[fa[y]][1]==y]=x; 90 son[x][c]=y; 91 fa[y]=x; 92 pushup(y); 93 pushup(x); 94 } 95 inline void put(int x,const int&d) 96 { 97 tag[x]+=d; 98 val[x]=val[x]*d; 99 ans[x]=ans[x]*d; 100 } 101 inline void pushdown(int x) 102 { 103 int d=tag[x]; 104 tag[x]=0; 105 put(son[x][0],d),put(son[x][1],d); 106 } 107 void down(int x) 108 { 109 if(!notroot(x)) 110 { 111 pushdown(x); 112 return; 113 } 114 down(fa[x]); 115 pushdown(x); 116 } 117 inline void splay(int x) 118 { 119 down(x); 120 while(notroot(x)) 121 { 122 int y=fa[x]; 123 if(!notroot(y)) 124 rotate(x); 125 else if((son[fa[y]][1]==y)==(son[y][1]==x)) 126 rotate(y),rotate(x); 127 else 128 rotate(x),rotate(x); 129 } 130 } 131 ll now; 132 inline ll add(int u) 133 { 134 splay(u); 135 son[u][1]=0; 136 pushup(u); 137 while(fa[u]) 138 { 139 int y=fa[u]; 140 splay(y); 141 son[y][1]=u; 142 pushup(y); 143 u=y; 144 } 145 splay(u); 146 ll x=ans[u].c; 147 put(u,1); 148 now=(now+ans[u].c-x+mod)%mod; 149 return now; 150 } 151 inline void init() 152 { 153 using namespace SAM; 154 for(int i=1;i<=SAM::tot;++i) 155 { 156 ll d=(t[i].len-t[t[i].fa].len); 157 ans[i]=val[i]=info(G2*d%mod,(mod-G2)*d%mod,0); 158 fa[i]=t[i].fa; 159 } 160 } 161 } 162 int main() 163 { 164 ios::sync_with_stdio(false); 165 cin>>n>>str; 166 for(int i=0;i<n;++i) 167 SAM::add(str[i]-'a',i); 168 LCT::init(); 169 ll tot=0; 170 for(int i=0;i<n;++i) 171 { 172 int pos=SAM::where[i]; 173 ans=((ans+LCT::add(pos)%mod+mod)%mod+mod)%mod; 174 cout<<ans<<'\n'; 175 } 176 return 0; 177 }