4567: [Scoi2016]背单词

4567: [Scoi2016]背单词

https://www.lydsy.com/JudgeOnline/problem.php?id=4567

题意:

  题意看了好久,最后在其他人的博客里看懂了的。

  n个字符串,给它们排一个顺序。花费最小。对于第x个位置字符串的花费如下计算是这样的:

  • 如果存在它的一个后缀单词在它的后面,花费为x*x
  • 如果它的所有后缀单词都在它前面了,花费为x-last_pos(last_pos为它的后缀单词最后一个出现的位置,如果没有则为0)。

分析:

  贪心 + dfs序。

  首先第一个花费一定是不优的。那就是一个单词在它的所有的后缀单词的后面。

  然后反着建出trie树。把非单词结尾的节点去掉,然后形成一棵树。现在就是给这棵树编号,花费为所有的Σid[x]-id[fa[x]]。

  贪心的思路:优先给siz小的子树编号。

  理解一下:假设现在又两棵子树,第一棵子树的大小为a,另一棵为b,(a<b)。先给a编号后,子树b的根就是a+1,Ans+=(a+b)-id[fa],先给b编号,子树a的根为b+1,Ans+=(b+1)-id[fa]。然后子树内部的点的花费与父节点的差,所以不论父节点是多少,按照最优的情况编号,不变。

代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<iostream>
 6 #include<cctype>
 7 #include<set>
 8 #include<vector>
 9 #include<queue>
10 #include<map>
11 #define fi(s) freopen(s,"r",stdin);
12 #define fo(s) freopen(s,"w",stdout);
13 using namespace std;
14 typedef long long LL;
15 
16 inline int read() {
17     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
18     for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
19 }
20 
21 const int N = 510001;
22 
23 int ch[N][26], dfn[N], siz[N], val[N], NowTime, Index;
24 char s[N];
25 vector<int> T[N];
26 LL Ans;
27 
28 void Insert(char *s,int n) {
29     int u = 1;
30     for (int i=n; i>=1; --i) {
31         int c = s[i] - 'a';
32         if (!ch[u][c]) ch[u][c] = ++Index;
33         u = ch[u][c];
34     }
35     val[u] = 1;
36 }
37 void build(int u,int fa) {
38     if (val[u]) T[fa].push_back(u);
39     for (int i=0; i<26; ++i) 
40         if (ch[u][i]) build(ch[u][i], val[u] ? u : fa);
41 }
42 bool cmp(int a,int b) {
43     return siz[a] < siz[b];
44 }
45 void dfs(int u,int fa) {
46     dfn[u] = ++NowTime;
47     if (u != 1) Ans += dfn[u] - dfn[fa];
48     sort(T[u].begin(), T[u].end(), cmp);
49     for (int i=0; i<T[u].size(); ++i) dfs(T[u][i], u);
50 }
51 int main() {
52     int n = read();
53     Index = 1;
54     for (int i=1; i<=n; ++i) {
55         scanf("%s",s + 1);
56         Insert(s, strlen(s + 1));
57     }
58     build(1, 1);
59     for (int i=Index; i>=1; --i) {
60         if (!val[i]) continue;
61         siz[i] = 1;
62         for (int j=0; j<T[i].size(); ++j) siz[i] += siz[T[i][j]];
63     }
64     dfs(1, 0);
65     cout << Ans;
66     return 0;
67 }
原文地址:https://www.cnblogs.com/mjtcn/p/9683210.html