Description

\(Lweb\) 面对如山的英语单词,陷入了深深的沉思,「我怎么样才能快点学完,然后去玩三国杀呢?」。这时候睿智的凤老师从远处飘来,他送给了 \(Lweb\) 一本计划册和一大缸泡椒,然后凤老师告诉 \(Lweb\) ,我知道你要学习的单词总共有 \(n\) 个,现在我们从上往下完成计划表,对于一个序号为 \(x\) 的单词(序号 \(1...x-1\) 都已经被填入):

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

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

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

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

Input

输入一个整数 ,表示 \(Lweb\) 要学习的单词数。接下来 \(n\) 行,每行有一个单词(由小写字母构成,且保证任意单词两两互不相同)。

Output

\(Lweb\) 吃的最少泡椒数。

Sample Input

2

a

ba

Sample Output

2

HINT

\(1 \leq n \leq 10^5\),所有字符的长度总和 \(1 \leq |len| \leq 510000\)


想法

把所有串反过来,建在 \(trie\) 树上,形成一颗前缀树,那么原题中的后缀即这里的前缀

一个显然的贪心是所有串都在它所有前缀填入后再填,\(n \times n\) 的代价要不起。

相当于在树上进行遍历,使所有点的父节点都在子节点前访问,总代价是所有点与其父节点的访问时间差 的总和。

于是又一个贪心,每个点访问后按它所有子节点 \(size\) 的大小,从小到大依次访问。

很容易想到这个贪心,但咋证明是对的呢?【听说这是个经典问题,但网上也没看见多少证明啊(逃】

可以看 \(neither_nor\) 的证明 https://blog.csdn.net/neither_nor/article/details/51362523


代码

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #include<cstring>
  5. using namespace std;
  6. const int N = 110000;
  7. typedef long long ll;
  8. int n;
  9. int fa[N],sz[N];
  10. struct trie{
  11. trie *ch[26];
  12. int id;
  13. }pool[N*5],*root;
  14. int cnt;
  15. void insert(char s[],int id){
  16. trie *p=root;
  17. int len=strlen(s);
  18. for(int i=len-1;i>=0;i--){
  19. if(!p->ch[s[i]-'a']) p->ch[s[i]-'a']=&pool[++cnt];
  20. p=p->ch[s[i]-'a'];
  21. }
  22. p->id=id;
  23. }
  24. struct node{
  25. node *nxt;
  26. int v;
  27. }pool2[N],*h[N];
  28. int cnt2;
  29. void addedge(int u,int v){
  30. node *p=&pool2[++cnt2];
  31. p->v=v;p->nxt=h[u];h[u]=p;
  32. }
  33. void dfs(trie *p,int cur){
  34. int nc=p->id ? p->id : cur;
  35. for(int i=0;i<26;i++)
  36. if(p->ch[i])
  37. dfs(p->ch[i],nc);
  38. if(p->id){
  39. sz[p->id]++;
  40. fa[p->id]=cur;
  41. sz[cur]+=sz[p->id];
  42. addedge(cur,p->id);
  43. }
  44. }
  45. ll ans;
  46. int b[N],tot;
  47. void work(int u){
  48. for(node *p=h[u];p;p=p->nxt) work(p->v);
  49. tot=0;
  50. for(node *p=h[u];p;p=p->nxt) b[++tot]=sz[p->v];
  51. sort(b+1,b+1+tot);
  52. for(int i=1;i<=tot;i++)
  53. ans+=1ll*b[i]*(tot-i);
  54. ans+=tot;
  55. }
  56. int main()
  57. {
  58. char s[N];
  59. scanf("%d",&n);
  60. root=&pool[++cnt];
  61. for(int i=1;i<=n;i++){
  62. scanf("%s",s);
  63. insert(s,i);
  64. }
  65. dfs(root,0);
  66. ans=0; work(0);
  67. printf("%lld\n",ans);
  68. return 0;
  69. }

[bzoj4567] [loj#2012] [SCOI2016] 背单词的更多相关文章

  1. 【BZOJ4567】[Scoi2016]背单词 Trie树+贪心

    [BZOJ4567][Scoi2016]背单词 Description Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?”.这时候睿智 的凤老师从远处飘来,他 ...

  2. BZOJ4567[Scoi2016]背单词

    4567: [Scoi2016]背单词 Time Limit: 10 Sec Memory Limit: 256 MB Submit: 304 Solved: 114 [Submit][Status] ...

  3. 【bzoj4567】[Scoi2016]背单词

    4567: [Scoi2016]背单词 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1123 Solved: 476[Submit][Status][ ...

  4. P3294 [SCOI2016]背单词

    P3294 [SCOI2016]背单词 Trie+贪心 倒插进树+取出重建+子树处理+贪心遍历 倒插进树:把后缀转化为前缀,所以把字符串倒着插进Trie中 取出重建:重新建立一棵以单词为节点的树,如果 ...

  5. 4567: [Scoi2016]背单词

    4567: [Scoi2016]背单词 https://www.lydsy.com/JudgeOnline/problem.php?id=4567 题意: 题意看了好久,最后在其他人的博客里看懂了的. ...

  6. [SCOI2016]背单词——trie树相关

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

  7. [SCOI2016]背单词 题解

    背单词 https://www.luogu.com.cn/problem/P3294 前言: Trie树的省选题(瑟瑟发抖QAQ) 问题汇总:(请忽略) (1)对Trie字典树的运用不熟练 (2)没想 ...

  8. [BZOJ4567][SCOI2016]背单词(Trie+贪心)

    1.题意表述十分难以理解,简单说就是:有n个单词,确定一个背的顺序,使总代价最小. 2.因为第(1)种情况的代价是n*n,这个代价比任何一种不出现第(1)种情况的方案都要大,所以最后肯定不会出现“背某 ...

  9. BZOJ4567 SCOI2016背单词(trie+贪心)

    倒过来变成查询前缀.考虑怎么排序.第一条代价n*n就相当于inf,说明一个单词的所有前缀都要排在它前面.那么串的依赖关系就是trie的结构.二三条说明代价是Σidi-idfa,那么显然最后的编号应该是 ...

随机推荐

  1. 基于koa2操作mysql封装例子

    新建better-mysql.js const mysql = require('mysql'); const config = require('../config/sqlConfig.js') l ...

  2. vue-learning:2 - template - directive

    指令 directive 在上一节我们知道,VUE的template模板通过VUE指令API实现与页面的视图层联系.所以本节将聚集在实现视图层交互的VUE指令系统directive的基础使用. 我们先 ...

  3. P1919 FFT加速高精度乘法

    P1919 FFT加速高精度乘法 传送门:https://www.luogu.org/problemnew/show/P1919 题意: 给出两个n位10进制整数x和y,你需要计算x*y. 题解: 对 ...

  4. If条件语句实战

    1.If条件判断语句 通常以if开头,fi结尾.也可加入else或者elif进行多条件的判断,if表达式如下: if (表达式) 语句1 else 语句2 fi 2.If常见判断逻辑运算符详解: -f ...

  5. jdbc的URL配置

    Microsoft SQL ServerMicrosoft SQL Server JDBC Driver (一般用来连接 SQLServer 2000)驱动程序包名:msbase.jar mssqls ...

  6. Ubuntu14.04虚拟机下基本操作(typical安装)

    1.打开终端:ctrl+alt+T 2.图形桌面和命令行界面切换:Ctrl+Alt+F1和Ctrl+Alt+F7 3.切换到root用户:激活前,sudo su+回车: 激活后,su+回车.  切换回 ...

  7. 17.python内置函数2

    python内置函数1:https://www.cnblogs.com/raitorei/p/11813694.html # max,min高级玩法 # l=[1,3,100,-1,2] # prin ...

  8. python类型常用整理

    # 一.数字 # int(..) # 二.字符串 # replace find join strip startswith split upper lower format # tempalte = ...

  9. RobotFramework+Appium 为了兼容iOS12,升级至Xcode10后,WebDriverAgent编译不通过:Undefind symbols for architecture x86_64

    报错信息如下: Undefined symbols for architecture arm64: "_OBJC_CLASS_$_XCElementSnapshot", refer ...

  10. 超简单!pytorch入门教程(一):Tensor

    http://www.jianshu.com/p/5ae644748f21 二.pytorch的基石--Tensor张量 其实标量,向量,矩阵它们三个也是张量,标量是零维的张量,向量是一维的张量,矩阵 ...