题目描述

Lweb 面对如山的英语单词,陷入了深深的沉思,”我怎么样才能快点学完,然后去玩三国杀呢?“。这时候睿智的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的:

—————序号 单词—————

1 2......n-2n-1 n—————

然后凤老师告诉 Lweb ,我知道你要学习的单词总共有 n 个,现在我们从上往下完成计划表,对于一个序号为 x 的单词(序号 1...x-1 都已经被填入):

1) 如果存在一个单词是它的后缀,并且当前没有被填入表内,那他需要吃 n*n 颗泡椒才能学会;

2) 当它的所有后缀都被填入表内的情况下,如果在 1...x-1 的位置上的单词都不是它的后缀,那么你吃 x 颗泡椒就能记住它;

3) 当它的所有后缀都被填入表内的情况下,如果 1...x-1的位置上存在是它后缀的单词,所有是它后缀的单词中,序号最大为 y ,那么你只要吃 x-y 颗泡椒就能把它记住。

Lweb 是一个吃到辣辣的东西会暴走的奇怪小朋友,所以请你帮助 Lweb ,寻找一种最优的填写单词方案,使得他记住这 n 个单词的情况下,吃最少的泡椒。

输入输出格式

输入格式:

输入一个整数 n ,表示 Lweb 要学习的单词数。接下来 n 行,每行有一个单词(由小写字母构成,且保证任意单词两两互不相同)1<=n<=100000, 所有字符的长度总和 1<=|len|<=510000

输出格式:

Lweb 吃的最少泡椒数

输入输出样例



输入样例#1:

2
a
ba


输出样例#1:

2


我们将“后缀”转化为“前缀”,那么很显然这是一个可以在$Trie$树上解决的问题。

我们建完$Trie$树后,我们建立一个根节点$0$,发现其实答案就是所有节点的编号与其父亲节点的编号差值和。

这个结论是建立在$1$号情况不能出现的情况下,因为$1$号情况代价最大,我们显然必须避开,并且一定能够避开。

那么现在问题就变成了给你一棵树,让你给节点编号(父节点编号一定比子节点小),求所有节点的编号与其父亲节点的编号差值和最小值。

很容易得到的一个贪心策略是:每次选深度最小的子树先标号。

简要证明一下:因为对于一个子树,其内部最优值一定是不变的,影响答案的只有与当前节点相邻节点的编号关系。那么我们一定先选子树$size$小的先编号,一定能得到最优值。

那么$dfs$的时候我们要选最小的$size$,如何实现?我们可以开个栈来按顺序存储要访问的节点,就可以了。



1 #include<iostream>
2 #include<cstdio>
3 #include<algorithm>
4 #include<cstring>
5 #include<stack>
6 using namespace std;
7 typedef long long ll;
8 const int M=550010;
9 const int N=200010;
10 struct Node
11 {
12 int next,to;
13 }edge[N+5];
14 ll ans;
15 int vis[N+5],n;
16 char s[M+5];
17 int trie[M+5][26],pd[M+5],pos,num,head[N+5],size[N+5],tot,tmp[N+5];
18 stack<int>S;
19 void add(int u,int v)
20 {
21 num++;
22 edge[num].next=head[u];
23 head[u]=num;
24 edge[num].to=v;
25 }
26 void insert(char *s,int len,int id)
27 {int i;
28 int x=0;
29 for (i=len-1;i>=0;i--)
30 {
31 if (!trie[x][s[i]-'a']) trie[x][s[i]-'a']=++pos;
32 x=trie[x][s[i]-'a'];
33 }
34 pd[x]=id;
35 }
36 void dfs(int x,int last)
37 {int i;
38 if (pd[x]) add(last,pd[x]),last=pd[x];
39 for (i=0;i<26;i++)
40 if (trie[x][i])
41 dfs(trie[x][i],last);
42 }
43 void get_size(int x)
44 {int i;
45 size[x]=1;
46 for (i=head[x];i;i=edge[i].next)
47 {
48 int v=edge[i].to;
49 get_size(v);
50 size[x]+=size[v];
51 }
52 }
53 bool cmp(int x,int y)
54 {
55 return size[x]<size[y];
56 }
57 void get_ans(int x,int fa)
58 {int i;
59 int top=0;
60 vis[x]=++tot;
61 ans+=vis[x]-vis[fa];
62 for (i=head[x];i;i=edge[i].next)
63 {
64 int v=edge[i].to;
65 tmp[++top]=v;
66 }
67 sort(tmp+1,tmp+top+1,cmp);
68 for (i=top;i>=1;i--)
69 S.push(tmp[i]);
70 while (top--)
71 {
72 i=S.top();
73 S.pop();
74 get_ans(i,x);
75 }
76 }
77 int main()
78 {int i;
79 cin>>n;
80 for (i=1;i<=n;i++)
81 {
82 scanf("%s",s);
83 insert(s,strlen(s),i);
84 }
85 dfs(0,0);
86 get_size(0);
87 get_ans(0,0);
88 cout<<ans;
89 }