题目大意
1≤n≤105,1≤∑|S|≤5.1×105
题目分析
题意差评。
显然最优解下,第一种情况我们显然不会让它发生。
如果我们将字符串之间的后缀关系连成一棵树,那么可以发现此题相当于给树分配一种序列,满足祖先必须在儿子之前,并且儿子与父亲位置差之和最小。
我们可以将所有串反过来构造一棵
Trie
解决连边问题(然而弱智的我考场上敲了个
AC
自动机,用
fail
树乱搞)。
考虑贪心策略,这题让我想到了小学奥数经常考的排队打水问题,要求等待时间之和尽量小,显然这两个问题是差不多的。因此我们可以先递归处理子树,然后将子树按
size
从小到大排序,依次分配子树根节点,这样一定最优。证明就大家自己细想吧,我是感性理解的~
时间复杂度
O(Llog2L)
。
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
typedef long long LL;
const int N=100050;
const int L=510050;
const int C=26;
int size[L],son[L];
LL f[L];
int n,l;
struct tree
{
int next[L],tov[L],last[L];
int tot;
void insert(int x,int y){tov[++tot]=y,next[tot]=last[x],last[x]=tot;}
}ft,t;
char s[L];
struct AC_automaton
{
int next[L][C],fail[L];
queue<int> q;
bool word[L];
int tot,root;
int newnode()
{
++tot;
for (int c=0;c<C;c++) next[tot][c]=-1;
return tot;
}
void init()
{
tot=-1;
root=newnode();
}
void insert()
{
int rt=root;
l=strlen(s);
for (int i=0;i<l;i++)
{
if (next[rt][s[i]-'a']==-1) next[rt][s[i]-'a']=newnode();
rt=next[rt][s[i]-'a'];
}
word[rt]=true;
}
void build()
{
for (int c=0;c<C;c++)
if (next[root][c]==-1) next[root][c]=0;
else fail[next[root][c]]=0,q.push(next[root][c]);
for (int x,c;!q.empty();)
{
x=q.front(),q.pop();
for (c=0;c<C;c++)
if (next[x][c]!=-1) fail[next[x][c]]=next[fail[x]][c],q.push(next[x][c]);
else next[x][c]=next[fail[x]][c];
}
for (int x=1;x<=tot;x++) ft.insert(fail[x],x);
}
void dfs(int x,int fa)
{
if (word[x]) t.insert(fa,x);
for (int i=ft.last[x];i;i=ft.next[i]) dfs(ft.tov[i],word[x]?x:fa);
}
}ACam;
bool cmp(int x,int y){return size[x]<size[y];}
void calc(int x)
{
size[x]=1;
for (int i=t.last[x],y;i;i=t.next[i])
calc(y=t.tov[i]),size[x]+=size[y];
son[0]=0;
for (int i=t.last[x];i;i=t.next[i])
son[++son[0]]=t.tov[i];
sort(son+1,son+1+son[0],cmp);
for (int i=1,cnt=1;i<=son[0];i++) f[x]+=f[son[i]]+cnt,cnt+=size[son[i]];
}
int main()
{
freopen("word.in","r",stdin),freopen("word.out","w",stdout);
ACam.init();
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",s);
ACam.insert();
}
ACam.build();
ACam.dfs(0,0);
calc(0);
printf("%lld\n",f[0]);
fclose(stdin),fclose(stdout);
return 0;
}