首先发现一个性质就是我们务必要求一个字符串的后缀串的编号必须在这个串前面。
对于这样的多串前后缀关系的问题,我们可以使用 Trie 树来解决,但是这里是后缀,于是翻转一下再用 Trie。
然后发现把这个要求建到 Trie 树上,然后缩点就是:给一个树标号,要求子节点编号必须大于父亲节点编号,并使得 (sum_{u} sum_{(u,v)in E}{w[u]-w[son]}) 最小。
发现答案就是按照子树 (siz) 排序过后的 (dfs) 序,比较简单,证明略,具体见题解。
代码:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
const int N=5e5+5;
int n,m,a[N],cur,DFN,siz[N],fa[N];
struct Trie{
int ch[26],tag;
#define ch(x,i) t[x].ch[i]
#define tag(x) t[x].tag
}t[N];
void Insert(char *str){
int len=strlen(str),p=0;
for(int i=0;i<len;i++){
int c=str[i]-'a';
if(!ch(p,c)) ch(p,c)=++cur;
p=ch(p,c);
}
tag(p)=true;
return ;
}
char s[N];
int head[N],to[N],nex[N],idx;
inline void add(int u,int v){
nex[++idx]=head[u];
to[idx]=v;
head[u]=idx;
return ;
}
void Dfs1(int x,int f){
siz[x]=1;
for(int i=0;i<26;i++) if(ch(x,i)) Dfs1(ch(x,i),tag(x)?x:f);
if(tag(x)) add(f,x),siz[f]+=siz[x],fa[x]=f;
return ;
}
#define PII pair<int,int>
#define ll long long
ll Ans;
int dfn[N];
void Dfs2(int x){
int top=0;
vector<PII> tmp;
if(x!=0) dfn[x]=++DFN;
if(fa[x]) Ans+=dfn[x]-dfn[fa[x]];
else Ans+=dfn[x];
for(int i=head[x];i;i=nex[i]) tmp.push_back(make_pair(siz[to[i]],to[i])),top++;
sort(tmp.begin(),tmp.end());
for(int i=0;i<top;i++) Dfs2(tmp[i].second);
return ;
}
int main(){
read(n);
for(int i=1;i<=n;i++) scanf("%s",s),reverse(s,s+strlen(s)),Insert(s);
Dfs1(0,0);
Dfs2(0);
write(Ans);
return 0;
}