参考博客:
https://www.luogu.org/problemnew/solution/P3294
https://blog.csdn.net/VictoryCzt/article/details/87186287
【题意】
题意如果看不懂,请到第二个链接去推一推事例,你就明白这个过程了。
来自题解中glf大佬的解析。
这题目描述真是令人窒息。
3个条件的意思大概是这样:
(1).如果有单词作为现在正在填入的单词的后缀但并未填入,将花费n*n的代价。
(2).如果没有单词作为当前填入单词的后缀,代价为当前填入单词序号x
(3).如果所有作为该单词的后缀的单词之前都已经填入,那么代价为当前序号x-最后一个作为当前单词的后缀的单词的序号y。
【题解】
读懂题以后这道题还是比较明显的贪心。第1个条件提示一定是先将所有作为后缀的单词填入,因为如果不这样填不管怎么样代价都小于n*n。
由于询问的是后缀,所以后缀相同其实等价于反串的前缀相同,所以倒着建立一个trie树。
这时问题转化为求一棵树的拓扑序,满足儿子与父亲的编号差的和最小,所以可以直接贪心来做,简单观察发现,对于某一刻,无论选哪个节点,总代价都会增大目前能扫到的第一个标记点的总量。
要使总代价最少,那么这次选的点一定要使以后增加的点最小.
所以记录一下每个点能看到的,以及这一个子树下分支总量,一定优先处理分支更小的子树。

1 #include<cstdio>
2 #include<cstring>
3 #include<vector>
4 #include<iostream>
5 #include<algorithm>
6 using namespace std;
7 const int N = 5e5 + 1e4 + 100;
8 const int M = N * 26;
9 long long ans = 0 ;
10 char s[M];
11 int n,idx;
12 int End[M],Son[N*26][26];
13 int pre[M],ID[M],Sz[M],num;
14 vector< int > G[N] ;
15 int Find( int x ){
16 if(pre[x]==x)return pre[x];
17 else return pre[x]=Find(pre[x]);
18 }
19 void Insert(int No){
20 int p = 0 ;
21 for(int i=strlen(s)-1; ~i ; i--){
22 int t = s[i] - 'a';
23 if( !Son[p][t] ) Son[p][t] = ++idx;
24 p = Son[p][t];
25 }
26 End[p] = No ;
27 }
28 void Build(int x ){
29 for(int i=0;i<26;i++){
30 int t = Son[x][i] ;
31 if( t ) {
32 if( !End[t] ){
33 pre[t] = Find(x);
34 }else{
35 G[End[Find(x)]].push_back(End[t]);
36 }
37 Build(t);
38 }
39 }
40 }
41 int cmp(int u, int v ){
42 return Sz[u] < Sz[v] ;
43 }
44 void dfs_Size( int u ){
45 Sz[u] = 1 ;
46 for(auto x : G[u] ){
47 dfs_Size(x);
48 Sz[u] += Sz[x];
49 }
50 sort ( G[u].begin() , G[u].end() , cmp );
51 }
52 void dfs_Sum(int u){
53 ID[u] = num ++ ;
54 for( auto x : G[u] ){
55 ans += num - ID[u];
56 dfs_Sum(x);
57 }
58 }
59 void Check(int x)
60 {
61 for( auto v : G[x] )
62 {
63 cout<<v<<endl;
64 Check(v);
65 }
66 }
67 int main(){
68 scanf("%d",&n);
69 for(int i=1;i<=n;i++){
70 scanf("%s",s);
71 Insert(i);
72 }
73 for(int i=1;i<=idx;i++){
74 pre[i] = i ;
75 }
76 Build(0) ;
77 dfs_Size(0);
78 dfs_Sum(0);
79 //Check(0);
80 printf("%lld\n",ans);
81 return 0;
82 }
