题目传送

阅读理解题题意解释可以看这位大佬的博客

  发现求后缀与倒序求前缀是等价的,而找前缀自然就想到了trie树。将所有字符串翻转后再建入trie树中,再对每一个字符串翻转后从trie树中找前缀,就能找到一个字符串的所有后缀了。

  由第三种情况知我们要想最小化总代价,则最小化一个字符串与最靠近它的后缀间的距离应该也是一个要考虑的因素。其实一个串最近的后缀其实一定是它的所有后缀中长度最大的,因为它的后缀中长度短的也一定是长度大的后缀,并且还要避免第一种情况的出现(即序列在一个字符串后面的串中不能有这个字符串的后缀)。故我们只要知道每个字符串最长的后缀就好。若一个串是另一个串的后缀,则可以从这个串向另一个串连一条有向边,则最终会形成一个森林。

  这时避免了第一种情况后,还有两种情况。我们做了这么多题,知道一种情况总应该比多种情况好做。故考虑将两种情况转化为一种。发现如果我们在所有题目给出的字符串的基础上再加入一个处于序列第0个位置的空串,那么第二种情况也就转化为了第三种情况,并且对答案没有影响。这种转化对于当前的森林,只要再建个标号为0的空串节点连向所有树的根就好了。

  此时问题就转化为:给树的节点编号,要求父亲节点的序号比儿子节点小,且根节点的序号为0,并使得儿子节点的编号减父亲节点编号的差的和最小,求最小的和。考虑怎么选点编号。作者一开始也懵的一逼于是看了看这位大佬博客关于选法的证明部分(“考虑建出……Q.E.D”)终于明白如果选到了一个点,就要一口气把它的子树都选了,并且优先选子树大小最小的儿子。dfs就搞定了呀。至于对某个点的儿子从子树大小小的往大的选,可以用堆维护。

具体实现请看代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<queue> #define max(a,b) ((a)>(b)?(a):(b)) using namespace std; const int N=,LEN=; int tree[][],cnt,n,l,dfs;
int ed[LEN],in[N],lst[N],to[N],nxt[N],ecnt,dfn[N]; long long siz[N],ans; string word[N]; char ch; inline int read()
{
int x=;
ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
} inline void getstring(string &a)
{
a="";
ch=getchar();
while(ch<'a'||ch>'z')
ch=getchar();
while(ch>='a'&&ch<='z')
a+=ch,ch=getchar();
} inline void insert(const string &a,int j)
{
l=a.length();
int now=,num;
for(int i=l-;i>=;--i)//要将字符串倒序插入trie树中
{
num=a[i]-'a';
if(!tree[now][num])
tree[now][num]=++cnt;
now=tree[now][num];
}
ed[now]=j;
} inline void addedge(int u,int v)
{
nxt[++ecnt]=lst[u];
lst[u]=ecnt;
to[ecnt]=v;
} inline int fin(const string &a)
{
int now=,num,ret=-;
l=a.length();
for(int i=l-;i>=;--i)//查后缀就是倒序查前缀
{
num=a[i]-'a';
if(!tree[now][num])
return ret;
now=tree[now][num];
if(ed[now]&&i)
ret=ed[now];
}
return ret;
} void dfssiz(int u)
{
siz[u]=;
for(int e=lst[u];e;e=nxt[e])
{
dfssiz(to[e]);
siz[u]+=siz[to[e]];
}
} struct node{
int lar,ord;
}head; inline bool operator < (const node &a,const node &b)
{
return a.lar>b.lar;//大根堆变小根堆
} void dfsans(int u)
{
priority_queue<node>hep;
for(int e=lst[u];e;e=nxt[e])
{
hep.push((node){siz[to[e]],to[e]});
}
while(!hep.empty())
{
head=hep.top();
hep.pop();
dfn[head.ord]=++dfs;//编号过程
ans+=dfn[head.ord]-dfn[u];
dfsans(head.ord);
}
} int main()
{
n=read();
for(int i=;i<=n;++i)
{
getstring(word[i]);//字符串的读入优化
insert(word[i],i);
}
int u;
for(int i=;i<=n;++i)
{
u=fin(word[i]);
if(u!=-)
{
addedge(u,i);
in[i]++;
}
}
for(int i=;i<=n;++i)
if(!in[i])//没有入度的点就是森林中树的根
addedge(,i);
dfssiz();//求一下每个点的子树大小。
dfsans();//统计答案。
printf("%lld",ans);
return ;
}

AC代码

洛谷P3294 [SCOI2016]背单词——题解的更多相关文章

  1. P3294 [SCOI2016]背单词

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

  2. [SCOI2016]背单词 题解

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

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

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

  4. [luogu] P3294 [SCOI2016]背单词 (贪心)

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

  5. 洛谷P3295 [SCOI2016]萌萌哒 题解

    洛谷P3295 [SCOI2016]萌萌哒 题目描述 公式粘过来就乱了,还是去洛谷看题吧 分析 如果暴力解决的话就是使用并查集把位数相同的数位并在一起.比如区间[1,2]和区间[3,4]的数字完全相同 ...

  6. BZOJ4567[Scoi2016]背单词

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

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

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

  8. 【bzoj4567】[Scoi2016]背单词

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

  9. 洛谷P1783 海滩防御 分析+题解代码

    洛谷P1783 海滩防御 分析+题解代码 题目描述: WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和 ...

随机推荐

  1. 设计模式:门面模式(Facade)

      前面介绍的适配器模式讲的是如何将一个接口转换成客户所需要的另一个接口,它的目的在于 解决接口的不兼容性问题.现在这里有这样一个模式,它的目的在于如何简化接口,它可以将多个类的复杂的一切隐藏在背后, ...

  2. Spring 启动时加载资源

    Spring加载资源文件目前了解三种, @PostConstruct在Context加载完成之后加载.在创建各个Bean对象之前加载. 实现ApplicationRunner的run方法,Bean加载 ...

  3. linux 三剑客之awk总结

    AWK 1.begin end使用 cat /tmp/passwd |awk -F ':' 'BEGIN {print "hello"} {print $1"\t&quo ...

  4. Git 实习一个月恍然大悟合集

    从开始实习到现在大概有一个月了,这个月时间接触了很多新东西,其中就包括了git版本控制.分支管理等等.我在这段时间里,深深地感受到了git对公司项目代码管理和控制.团队合作带来的益处和其重要性.其实在 ...

  5. MySQL中出现的小问题

    ./scripts/mysql_install_db: line 249: /app/mysql/bin/my_print_defaults: cannot execute binary fileNe ...

  6. split、paste命令

    一.split分割文件 语法       split [OPTION] ... [INPUT [PREFIX]] 描述       将固定大小的INPUT输出到PREFIXaa,PREFIXab,.. ...

  7. SQL----Scalar 函数

    UCASE() 函数 UCASE 函数把字段的值转换为大写. SQL UCASE() 语法 SELECT UCASE(column_name) FROM table_name SQL UCASE() ...

  8. Exited too quickly (process log may have details)-配置问题

    在配置supervisor的时候,提示Exited too quickly (process log may have details),这个时候一脸懵逼,啥回事,执行太快了???

  9. linux下mysql忘记密码解决方案

    一.写随笔的原因:之前自己服务器上的mysql很久不用了,忘记了密码,所以写一下解决方案,以供以后参考 二.具体的内容: 1. 检查mysql服务是否启动,如果启动,关闭mysql服务 运行命令:ps ...

  10. concurrent.futures:线程池,让你更加高效、并发的处理任务

    并发任务池 concurrent.futures模块提供了使用工作线程或进程池运行任务的接口. 线程池和进程池的API是一致的,所以应用只需要做最小的修改就可以在线程和进程之间进行切换 这个模块提供了 ...