背单词

https://www.luogu.com.cn/problem/P3294

前言:

Trie树的省选题(瑟瑟发抖QAQ)

问题汇总:(请忽略)

(1)对Trie字典树的运用不熟练

(2)没想到可以将后缀转换为前缀来解决

(3)对于并查集和vector不知道怎么加到这个程序中


题目简述:

PS:这道题话有点多,有点乱,建议借用草稿本理解题意

给你n个互不相同的单词,学习这些单词要按照给定的规则吃泡椒,要求吃的泡椒数最少

吃泡椒规则:(设当前单词为now,且1~now-1都已经记忆过)

(1)如果存在一个单词是now的后缀且没有记忆过,那么学习now就要吃n×n颗泡椒

(2)如果该单词的所有后缀都记忆过但都不在1~now-1的位置上,则要吃now颗泡椒

(3)如果该单词的所有后缀记忆过且1~now-1中有now的后缀单词,设1~now-1中离

now最近的为last,则只要吃now-last颗泡椒


算法:

贪心、Trie字典树、DFS深搜、并查集(并查集也许可以不用)


简化规则+算法分析:

1、简化规则:规则初看好复杂,但是仔细推敲一下,可以发现这个规则是可以简化的。

(1)第一种需要n×n颗泡椒的情况,对于求最小值来说,肯定是要杜绝的。所以这种情况我们就直接忽略不计

(2)其实原规则中的二三点有相似之处,差别就是now的后缀单词是否在now之前。原规则的第二点,其实可以看做last在0位置,则last-now=now,是符合题意的。所以,我们只需要考虑将now的后缀单词在now之前记忆,并使last尽量接近now

综上,三大条规则就简化为一种贪心:将now的后缀单词在now之前记忆,并使last尽量接近now(重点!)

2、算法分析(重点!!)

(1)由简化规则,我们就可以得出要使用贪心(其实根据题意的求最小值也能得出)

(2)后缀怎么处理呢?后缀数组(并不会)?其实转换一下思想,将字符串倒序一下,后缀不就变成了前缀吗?!那处理前缀怎么办?肯定优先想到Trie字典树呀!于是我们得出了第二个算法

(3)建立了Trie树后,我们要遍历树并求距离(泡椒数),且要求距离和最小,这时,就有一个dfs序的东西。所以我们就用dfs序+子树从小到大排序来解决最小值问题

PS:这道题关于dfs序的正确性证明,我也不是很懂,只能讲个大概:所有的size排序后,第i个根节点的代价是前面所有树的size之和,所以要dfs序。(且有时候并不只是dfs序可以解决最小值,其他序列也同样可以)

(4)对于子树排序,还要用到STL容器中的vector,并查集也是辅助建立

vector的


代码理解:

注意:

(1)tot初始值为1,否则程序运行错误

(2)每个数组的大小一定要开到510000以上(因为所有字符的长度总和 1<=|len|<=510000),否则会RE

(3)ans一定要开long long,不然会爆int的内存,导致一个点以上WA掉

#include <bits/stdc++.h>
using namespace std;
int n,tot=1,sum;
long long ans;
int ch[510001][26],fa[510001],son[510001],id[5100001],bo[5100001];
char word[510001];
vector<int> shan[510001]; void insert(char *s,int now) { //建trie树
int u=1,len=strlen(s);
for(register int i=len-1;i>=0;i--) {
int c=s[i]-'a';
if(!ch[u][c]) ch[u][c]=++tot;
u=ch[u][c];
}
bo[u]=now;
} int find_fa(int x) { //并查集
if(fa[x]==x) return fa[x];
else return fa[x]=find_fa(fa[x]);
} void find_put(int x) { //不断遍历儿子,将每棵树装入vector中,方便排序
for(int i=0;i<26;i++) {
int net=ch[x][i];
if(net!=0) {
if(bo[net]==0) {
fa[net]=find_fa(x);
}
else shan[bo[find_fa(x)]].push_back(bo[net]);
find_put(net);
}
}
} bool cmp(int x,int y) {
return son[x]<son[y];
} void find_son(int x) { //找每个节点的儿子个数,然后根据树的大小进行排序
son[x]=1;
for(vector<int>::iterator it=shan[x].begin();it!=shan[x].end();it++) {
int v=*it;
find_son(v);
son[x]+=son[v];
}
sort(shan[x].begin(),shan[x].end(),cmp);
} void dfs(int x) { //dfs
id[x]=sum++; //该节点的深度
for(vector<int>::iterator it=shan[x].begin();it!=shan[x].end();it++) {
int v=*it;
ans+=sum-id[x]; //累加吃泡椒数
dfs(v);
}
} int main() {
scanf("%d",&n);
for(register int i=1;i<=n;i++) {
scanf("%s",word);
insert(word,i);
}
for(register int i=1;i<=tot;i++) fa[i]=i;
find_put(1);
find_son(0);
dfs(0);
printf("%lld",ans);
return 0;
}

参考+基础:洛谷 Tian_Xing大佬的题解 (跪谢orz)


[SCOI2016]背单词 题解的更多相关文章

  1. BZOJ4567:[SCOI2016]背单词——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4567 Lweb 面对如山的英语单词,陷入了深深的沉思,“我怎么样才能快点学完,然后去玩三国杀呢?” ...

  2. 洛谷P3294 [SCOI2016]背单词——题解

    题目传送 阅读理解题题意解释可以看这位大佬的博客. 发现求后缀与倒序求前缀是等价的,而找前缀自然就想到了trie树.将所有字符串翻转后再建入trie树中,再对每一个字符串翻转后从trie树中找前缀,就 ...

  3. BZOJ4567[Scoi2016]背单词

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

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

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

  5. 【bzoj4567】[Scoi2016]背单词

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

  6. P3294 [SCOI2016]背单词

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

  7. 4567: [Scoi2016]背单词

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

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

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

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

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

随机推荐

  1. Java实现 LeetCode 447 回旋镖的数量

    447. 回旋镖的数量 给定平面上 n 对不同的点,"回旋镖" 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺 ...

  2. Java实现 LeetCode 235 二叉搜索树的最近公共祖先

    235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个 ...

  3. java实现第五届蓝桥杯出栈次序

    出栈次序 X星球特别讲究秩序,所有道路都是单行线.一个甲壳虫车队,共16辆车,按照编号先后发车,夹在其它车流中,缓缓前行. 路边有个死胡同,只能容一辆车通过,是临时的检查站,如图[p1.png]所示. ...

  4. Java实现第八届蓝桥杯纸牌三角形

    纸牌三角形 题目描述 A,2,3,4,5,6,7,8,9 共9张纸牌排成一个正三角形(A按1计算).要求每个边的和相等. 下图就是一种排法(如有对齐问题,参看p1.png). A 9 6 4 8 3 ...

  5. 一文了解Docker容器技术的操作

    一文了解Docker容器技术的操作 前言一.Docker是什么二.Docker的安装及测试Docker的安装Docker的Hello world测试三.Docker的常见操作镜像的基本操作容器的基本操 ...

  6. Crypto++ AES 加密解密流程

    // aesdemo.cpp : 定义控制台应用程序的入口点. // #include <stdio.h>#include <tchar.h>#include <iost ...

  7. 今天抠图,Python实现一键换底片!想换什么换什么(附源码)

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 生活中我们会拍很多的证件照,有的要求红底,有的是白底,有的是蓝底,今天不通 ...

  8. windows下Python版本切换使用方法

    由于历史原因,Python有两个大的版本分支,Python2和Python3,又由于一些库只支持某个版本分支,所以需要在电脑上同时安装Python2和Python3,因此如何让两个版本的Python兼 ...

  9. 曹工改bug:centos下,mongodb开机不能自启动,systemctl、rc.local都试了,还是不行,要不要放弃?

    问题背景 最近装个centos 7.6的环境,其中,基础环境包括,redis.nginx.mongodb.fastdfs.mysql等,其中,自启动使用的是systemctl,其他几个组件,都没啥问题 ...

  10. 一文梳理JS事件

    JavaScript与HTML的交互是通过事件进行的.事件,就是文档或浏览器窗口发生的一些特定的交互瞬间. 事件流 事件捕获 事件冒泡 事件处理程序 事件委托 1. 事件流 如果单机页面上的某个按钮, ...