http://www.lydsy.com/JudgeOnline/problem.php?id=4567
https://www.luogu.org/problem/show?pid=3294
比较有趣的Trie题目之一(而且还挺新的)
我们可以按照各串的后缀建一棵Trie树,然后dfs序遍历出需要的点并标记(指那些位于各串结尾的节点,这些是有用的)
接着我们来考虑题目中的三种情况:
- 如果存在一个单词是它的后缀,并且当前没有被填入表内,那他需要吃 n*n 颗泡椒才能学会;
–这个因为代价太大,如果有这种情况绝对不是最优,不考虑 - 当它的所有后缀都被填入表内的情况下,如果在 1…x-1 的位置上的单词都不是它的后缀,那么你吃 x 颗泡椒就能记住它;
–这个需要考虑 - 当它的所有后缀都被填入表内的情况下,如果 1…x-1的位置上存在是它后缀的单词,所有是它后缀的单词中,序号最大为 y ,那么你只要吃 x-y 颗泡椒就能把它记住。
–这个需要考虑
因为不考虑1,即每个后缀填完后才能填自己,同时呢填完一个单词的子树的最小代价是固定的(自己yy一下)
所以只要合理安排儿子的遍历顺序即可。
这题就转化成了贪心
显然的,我们就应该先遍历子树较小的儿子,这样总代价最小(贪心嘛)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll ans=0;
int s[500010],fa[5000010],a[500010],h[500010],son[500010];
char c[1000001];
bool b[500001]={0};
int n,p[500010][26]={0},np=1;
inline void insert(int x){
int now=1,l=strlen(c+1);
for(int i=l;i;i--){
int t=c[i]-'a';
if(!p[now][t])p[now][t]=++np;
now=p[now][t];s[now]++;
}
b[now]=1;
}
inline void dfs(int x){
int father=fa[x];
if(b[x]){
h[x]=a[fa[x]];a[fa[x]]=x;father=x;
}
for(int i=0;i<26;i++)if(p[x][i]){
fa[p[x][i]]=father;dfs(p[x][i]);
}
}
inline void getans(int x){
int sum=0;
for(int i=a[x];i;i=h[i])sum++,son[sum]=s[i];
sort(son+1,son+sum+1);
s[x]=1;
for(int i=1;i<=sum;i++)ans+=s[x],s[x]+=son[i];
for(int i=a[x];i;i=h[i])getans(i);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",c+1);
insert(i);
}
fa[1]=1;dfs(1);getans(1);
printf("%lld",ans);
return 0;
}