描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。
神奇的是小Hi发现了一部名字叫《十进制进行曲大全》的作品集,顾名思义,这部作品集里有许多作品,但是所有的作品有一个共同特征:只用了十个音符,所有的音符都表示成0-9的数字。
现在小Hi想知道这部作品中所有不同的旋律的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0)。答案有可能很大,我们需要对(10^9 + 7)取摸。
分析
- 首先这是很多个字符串,并且你要考虑的是在整个字符集内本质不同的子串,所以不要想着把每个串都做一遍后缀自动机。
- 那么这道题就是一个广义后缀自动机了,处理方法就是每次插入完一个串以后,让la=1就好了。(当然你在每两个串中间插入一个特殊字符也是可以的)。
- 现在就是计算答案了。考虑我们是要把所有本质不同的子串当成十进制数,累加到答案里面。那么应该是在后缀自动机的图上搞事情,和parent树没什么关系。
- 设sum[i]表示从 t 0 t_0 t0到i这个节点,所有本质不同的子串累加的答案。这个显然要加到答案里面。然后置于如何递推? s u m [ n e [ i ] [ c ] ] + = s u m [ i ] ∗ 10 + c ∗ n u m [ i ] sum[ne[i][c]]+=sum[i]*10+c*num[i] sum[ne[i][c]]+=sum[i]∗10+c∗num[i],其中 n u m [ i ] num[i] num[i]是从 t 0 t_0 t0到i这个点,有多少个本质不同的子串。
- 显然需要做一个拓扑。然后本题就算做完。
- 哎,虽然思路很简单,但是我写代码自带bug啊,服了。记得,如果是在两个字符串中间加特殊字符了,那么把所有入度为0的点都先加入队列。如果是每次把la=1,那么最开始只需要把1加入队列就可以。(具体原因自己想一下)。每一个数的范围一定要想清楚,不然你会暴毙的。
Coding
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define rint register int
using namespace std;
const int N=2e6+100;
const int mod=1e9+7;
ll t,n,tot=1,la=1,siz[N],len[N],fa[N],ne[N][30],A[N],c[N],num[N];
char s[N];ll sum[N],ans;
queue<int>q;int rd[N];
void add(int c){
rint p=la,np=la=++tot;siz[tot]=1;len[tot]=len[p]+1;
for(;p&&!ne[p][c];p=fa[p]) ne[p][c]=np;
if(!p) fa[np]=1;
else{
rint q=ne[p][c];
if(len[p]+1==len[q]) fa[np]=q;
else{
rint nq=++tot;
memcpy(ne[nq],ne[q],sizeof(ne[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
len[nq]=len[p]+1;
for(;p&&ne[p][c]==q;p=fa[p]) ne[p][c]=nq;
}
}
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%s",s+1);
n=strlen(s+1);la=1;
for(int i=1;i<=n;++i) add(s[i]-'0');
}
num[1]=1;
for(int i=1;i<=tot;++i){
for(int j=0;j<10;++j)
if(ne[i][j])
rd[ne[i][j]]++;
}
for(int i=1;i<=tot;++i) if(!rd[i]) q.push(i);
//q.push(1);
while(q.size()){
int x=q.front();q.pop();
//cout<<x<<' '<<sum[x]<<endl;
for(int j=0;j<10;++j){
int nq=ne[x][j];
if(!nq) continue;
num[nq]=(num[x]+num[nq])%mod;rd[nq]--;
sum[nq]=((sum[nq]+1LL*sum[x]*10%mod)%mod+1LL*num[x]*j%mod)%mod;
if(!rd[nq]) q.push(nq);
}
}
for(int i=1;i<=tot;++i) ans=(ans+sum[i])%mod;
//for(int i=1;i<=tot;++i) cout<<num[i]<<endl;
cout<<ans<<endl;
return 0;
}