[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
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\n", ans); 91 } 92 int main() { 93 work(); 94 return 0; 95 }
博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/NaVi-Awson/,否则你会终生找不到妹子!!!