[SCOI 2016]背单词

Description

Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”。这时候睿智
的凤老师从远处飘来,他送给了 Lweb 一本计划册和一大缸泡椒,他的计划册是长这样的:
—————
序号  单词
—————
 1
 2
……
n-2
n-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 个单词的情况下,吃最少的泡椒。

Input

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

Output

 Lweb 吃的最少泡椒数

Sample Input

2
a
ba

Sample Output

2

题目大意

给你$n$个字符串,不同的排列有不同的代价,代价按照如下方式计算(字符串$s$的位置为$x$):
1.排在$s$后面的字符串有$s$的后缀,则代价为$n^2$;
2.排在$s$前面的字符串有$s$的后缀,且没有排在$s$后面的$s$的后缀,则代价为$x-y$($y$为最后一个与$s$不相等的后缀的位置);
3.$s$没有后缀,则代价为$x$。
求最小代价和。

题解

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

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

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

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

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

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

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

 1 //It is made by Awson on 2017.10.10
 2 #include <set>
 3 #include <map>
 4 #include <cmath>
 5 #include <ctime>
 6 #include <cmath>
 7 #include <stack>
 8 #include <queue>
 9 #include <vector>
10 #include <string>
11 #include <cstdio>
12 #include <cstdlib>
13 #include <cstring>
14 #include <iostream>
15 #include <algorithm>
16 #define LL long long
17 #define Min(a, b) ((a) < (b) ? (a) : (b))
18 #define Max(a, b) ((a) > (b) ? (a) : (b))
19 #define sqr(x) ((x)*(x))
20 using namespace std;
21 const int M = 510000;
22 const int N = 100000;
23 
24 int n;
25 LL ans;
26 int trie[M+5][26], val[M+5], pos;
27 char s[M+5];
28 struct tt {
29   int to, next;
30 }edge[N+5];
31 int path[N+5], top;
32 int size[N+5], vis[N+5], tot;
33 struct ss {
34   int size, id;
35   bool operator < (const ss &b) const{
36     return size > b.size;
37   }
38 }tmp[N+5];
39 stack<int>S;
40 
41 void add(int u, int v) {
42   edge[++top].to = v;
43   edge[top].next = path[u];
44   path[u] = top;
45 }
46 void dfs(int u, int last) {
47   ++tot;
48   if (val[u]) add(last, val[u]), last = val[u];
49   for (int i = 0; i < 26; i++)
50     if (trie[u][i])
51       dfs(trie[u][i], last);
52 }
53 void insert(char *s, int len, int num) {
54   int u = 0;
55   for (int i = len-1; i >= 0; i--) {
56     if (!trie[u][s[i]-'a']) trie[u][s[i]-'a'] = ++pos;
57     u = trie[u][s[i]-'a'];
58   }
59   val[u] = num;
60 }
61 void get_size(int u) {
62   size[u] = 1;
63   for (int i = path[u]; i; i = edge[i].next) {
64     get_size(edge[i].to);
65     size[u] += size[edge[i].to];
66   }
67 }
68 void get_ans(int u, int fa) {
69   int pos = 0; vis[u] = ++tot; ans += vis[u]-vis[fa];
70   for (int i = path[u]; i; i = edge[i].next) {
71     tmp[++pos].size = size[edge[i].to];
72     tmp[pos].id = edge[i].to;
73   }
74   sort(tmp+1, tmp+1+pos);
75   for (int i = 1; i <= pos; i++) S.push(tmp[i].id);
76   while(pos--) {
77     int v = S.top(); S.pop();
78     get_ans(v, u);
79   }
80 }
81 void work() {
82   scanf("%d", &n);
83   for (int i = 1; i <= n ;i++) {
84     scanf("%s", s);
85     insert(s, strlen(s), i);
86   }
87   dfs(0, 0);
88   get_size(0);
89   get_ans(0, 0);
90   printf("%lld
", ans);
91 }
92 int main() {
93   work();
94   return 0;
95 }
 
原文地址:https://www.cnblogs.com/NaVi-Awson/p/7648048.html