题目链接:https://hihocoder.com/problemset/problem/1457
注意:不能在遍历和统计入度的时候直接删除‘10’的边,这样会导致入度统计出错。
也不能用 maxlen[u] - maxlen[fa[u]] 来计算tot,因为‘10’ 边会影响结果。
#include <bits/stdc++.h>
#define ll long long
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define pb push_back
const int N = 1e6+100;
const int mod = 1e9+7;
using namespace std;
queue<int>q;
ll dp[N<<1],tot[N<<1];
struct SAM{
int cnt,last,fa[N<<1],son[N<<1][30],l[N<<1],siz[N<<1],deg[N<<1];
SAM(){
cnt = 1;
fa[1] = 0;
last = 1;
memset(son,0,sizeof(son));
}
void ins(int x){
int u = last,now = last = ++cnt; //last不一定是cnt
l[now] = l[u]+1; //新加点now
for(;u&&!son[u][x];u=fa[u]) son[u][x] = now; //向上寻找第一个有x的父亲
if(!u) fa[now] = 1; //如果找到根节点
else {
int v = son[u][x];
if(l[u]+1==l[v]) fa[now] = v; //判断该父亲的x儿子是否合法
else{
int vv = ++cnt; //分割节点,制造合法儿子
l[vv] = l[u]+1; //合法长度
memcpy(son[vv],son[v],sizeof(son[v])); //复制信息
fa[vv] = fa[v];
fa[now] = fa[v] = vv;
for(;u&&son[u][x]==v;u=fa[u]) son[u][x] = vv;
}
}
siz[now] = 1;
}
int sum[N<<1],tp[N<<1];
void topo(){
int i;
memset(sum,0,sizeof(sum));
for(i = 1;i <= cnt;i ++) sum[l[i]] ++;
for(i = 1;i <= cnt;i ++) sum[i] += sum[i-1];
for(i = 1;i <= cnt;i ++) tp[sum[l[i]]--] = i;
for(i = cnt;i >= 1;i --) {
int v = tp[i];
siz[fa[v]] += siz[v]; //计算r集的大小
}
}
ll solve() {
rep(i, 1, cnt) rep(j, 0, 10) deg[son[i][j]]++;
q.push(1);
tot[1] = 1;
while(!q.empty()) {
int u = q.front();
q.pop();
rep(i, 0, 10) {
int v = son[u][i];
if(v==0) continue;
if(i!=10) {
tot[v] += tot[u];
tot[v] %= mod;
dp[v] += dp[u]*10+i*(tot[u]);
dp[v] %= mod;
}
deg[v]--;
if(deg[v]==0) q.push(v);
}
}
ll ans = 0;
rep(i, 1, cnt) ans = (ans+dp[i])%mod;
return ans;
}
}sam;
int main(){
//freopen("a.txt","r",stdin);
ios::sync_with_stdio(0);
int n;
cin>>n;
rep(i, 1, n) {
string s;
cin>>s;
for(auto v:s)sam.ins(v-'0');
sam.ins(10);
}
sam.topo();
cout<<sam.solve();
return 0;
}