观前须知

本题解全部内容遵循CC BY-NC-SA 4.0 Deed原则

更好的观看体验 点这里

笔者的博客主页

正文

Luogu P3294 【SCOI2016】背单词

笔者在刷题的时候看到了这道好题

花了四十分钟切掉以后,看了一下题解

发现自己的想法不太一样

所以想做一篇适合我这样的蒟蒻看的题解awa

那么,我们开始吧~

首先

题意理解

(笔者认为本文最难的一个部分)

给你 \(n\) 个字符串

要求你找一种这 \(n\) 个字符串的排列

规则一:若一个字符串 \(a\) 有一个字符串 \(b\) 为 \(a\) 的后缀,

(这里的后缀在 \(n\) 个字符串中出现过,且由题意不为该串本身,下文同理)

且 \(b\) 排在 \(a\) 前,则花费增加 \(n^2\)

规则三:若一个字符串 \(a\) 的所有后缀都排在 \(a\) 前,

则花费增加 \(a\) 到最近一个 \(a\) 的后缀 \(b\) 的距离(即 \(x-y\) )

规则二:特别地,若 \(a\) 没有后缀,则花费增加 \(a\) 的排名(即 \(x\) )

求最小花费

(吐槽一下,原题真的很不好理解,笔者这里看了十分钟都以为是题目给定了排列顺序)

那么来简化题意

首先,发现原题中的规则二就是规则三的特例,所以不需要考虑

然后,可以发现规则一增加的 \(n^2\) 实在太多了(因为每个规则三最多也只能增加 \(n\) 的花费)

所以不能违反规则一

所有字符串的后缀一定排在这个字符串的前面

(这种情况是一定能完成的,按照字符串长度排序就是一种方案)

那么题意已经变为了

在不违反规则一的情况下

使规则三的花费和最小

建模

发现不能违反规则一后

规则三中的 最近一个后缀 变为了 长度最大的一个后缀

发现每个字符串要么有唯一的一个长度最大的一个后缀,要么没有后缀

这和的结构类似

那么我们可以建立一棵树,满足任意一个节点都是它的所有儿子的长度最大的后缀

(也就是SAM中的后缀树)

对于没有后缀的点,我们建立一个虚根(代码中为0号点),作为它们的父亲

(这里的虚根可以理解为是一个空串,因为空串是每一个字符串的后缀)

下面给出了一棵后缀树方便大家理解:

a ab ba aab aba ababa bbaab bbbbba

建好这棵树后,我们就可以开始贪心

贪心

先直接说贪心策略:

在后缀树上按照dfs序选点

且每个节点先走子树小的

(接下来的证明可以感性理解,建议边想边画图)

首先证明dfs序选点是正确的

对于任意两棵要决定先后顺序的子树

若两者有祖先关系,根据规则一显然深度小的优先

否则由于上述原因,选深度较大的节点要求必须先选它的祖先

所以我们只需证明,对于两个深度相同的子树,我们要先选完一棵子树,再选择另一棵子树

不妨设先选的子树的树根为 \(x\),后选的子树的树根为 \(y\)

首先考虑把 \(y\) 插入到 \(x\) 的子树选完前先选

设 \(y\) 提前了 \(a\) 个位置

对于 \(y\) 子树内的第一个子节点,花费增加了 \(a\)

对于插入 \(y\) 后的第一个节点,花费增加了 \(1\)

继续把 \(y\) 的子树内的节点提前,花费不变

所以不按dfs序选点是更劣的

接下来证明要先走子树小的

对于一个节点,考虑它的每一个孩子

发现可以递归处理每一个孩子,那么我们只需要考虑每个孩子到这个节点的距离

根据上面的结论可得,每个孩子到这个节点的距离就是在这个节点中已经走过的节点数

那么为了距离和最小,显然要走过的节点数尽量小

所以子树小的优先选

好的,那么我们的答案就算出来了

欸?你问我是不是少了些什么?

好吧

最后一部分

建树

为了建树,我们只需要求出每个节点的父节点

即每个字符串的最长后缀

我们先根据字符串长度排序

那么每个字符串的后缀都在这个字符串前面了

但是后缀不好做

所以我们把每个字符串都倒过来变成前缀

我们把每个字符串依次插入到Trie树

并在每个字符串的终止结点记录编号

我们可以惊喜地发现:

对于一个字符串的最长后缀(这里已经变为前缀了)

就是在这个字符串在Trie树上的对应路径中

深度最大的终止节点

那么我们就能很容易地求出每个节点的父亲

那么就可以建树了

(这块讲的比较抽象,建议配着代码实用,或自己think一下)

一些小细节:

用vector存树方便sort

按照字符串长度排好的顺序其实是后缀树的拓扑逆序,可以直接倒序枚举更新sz

因为字符串长度不确定所以不能用scanf和char数组了(悲)

#include<bits/stdc++.h>

using namespace std;

static constexpr int AwA = 1e5 + 10;
static constexpr int PwP = 6e5 + 10; int n;
//因为这道题每个字符串长度不确定,所以我只能抛弃我的char数组了(悲)
string s[AwA];
//记录每个节点算出来的父亲
int fa[AwA];
//字典树,id[u]!=0时记录该节点对应的字符串编号
int ch[PwP][26], id[PwP], tot = 1; vector<int> tr[AwA];
int sz[AwA];
long long ans; //贪心选点
void Dfs(int u) {
int cur = 1;
for (int v: tr[u]) {
Dfs(v);
ans += cur;
cur += sz[v];
}
} int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr); cin >> n;
for (int i = 1; i <= n; i++) cin >> s[i];
//根据字符串长度排序
sort(s + 1, s + n + 1, [](auto &s1, auto &s2) { return s1.size() < s2.size(); }); int u, p;
for (int i = 1; i <= n; i++) {
//如果路径上没有终止节点,即没有后缀,则父亲为虚根0
// fa[i]=0;
u = 1;
//倒叙枚举,变后缀为前缀
for (auto k = s[i].rbegin(); k != s[i].rend(); k++) {
p = *k - 'a';
if (!ch[u][p]) ch[u][p] = ++tot;
u = ch[u][p];
//遇到终止节点更新父亲
if (id[u]) fa[i] = id[u];
}
//记录终止节点
id[u] = i;
} //因为父亲串的长度一定小于儿子,所以根据字符串长度排序后为拓扑逆序
for (int i = n; i; i--) sz[i]++, sz[fa[i]] += sz[i];
//建树
for (int i = 1; i <= n; i++) tr[fa[i]].push_back(i);
//按子树大小排序,方便贪心选择
//注意0节点也要排序
auto cmp = [&](int i, int j) { return sz[i] < sz[j]; };
for (int i = 0; i <= n; i++) sort(tr[i].begin(), tr[i].end(), cmp);
Dfs(0);
printf("%lld\n", ans);
return 0;
}

希望这篇题解能帮助你更好地理解这道很好的贪心题

完结撒花!~

Luogu P3294 背单词的更多相关文章

  1. luogu P2353 背单词

    二次联通门 : luogu P2353 背单词 一眼看过去, 卧槽,AC自动机板子题 写完后T成SB 卧槽10^6 做个篮子啊 重构思路... 恩..Hash + 莫队... 恶心啊.. 找xxy d ...

  2. P3294 [SCOI2016]背单词

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

  3. [SCOI2016]背单词 题解

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

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

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

  5. 【SCOI2016】背单词

    P3294[SCOI2016]背单词 [提示] 这道题大概是告诉我们,让我们用一堆n个单词安排顺序,如果当前位置为x,当前单词的后缀没在这堆单词出现过,代价就为x,这里的后缀是原意,但不算自己(不算本 ...

  6. 做中学(Learning by Doing)之背单词-扇贝网推荐

    做中学(Learning by Doing)之背单词-扇贝网推荐 看完杨贵福老师(博客,知乎专栏,豆瓣)的「继续背单词,8个月过去了」,我就有写这篇文章的冲动了,杨老师说: 有时候我会感觉非常后悔,如 ...

  7. “我爱背单词”beta版发布与使用说明

    我爱背单词BETA版本发布 第二轮迭代终于画上圆满句号,我们的“我爱背单词”beta版本已经发布. Beta版本说明 项目名称 我爱背单词 版本 Beta版 团队名称 北京航空航天大学计算机学院  拒 ...

  8. BZOJ4567[Scoi2016]背单词

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

  9. 《我爱背单词》 Alpha版 发布说明

    ——发布地址(baidu网盘) http://pan.baidu.com/s/15omtB ——简介  <我爱背单词>是一款英语单词记忆和管理辅助软件,旨在帮助广大考生在短期内攻克GRE. ...

  10. [No000057]一个人默默背单词,小心被传染哦

    不日凛冬将至,全国各地,已有多名少侠因季节变化,出现了不同程度的四肢不勤.bd不分的症状.具体表现为—— 包大人在此高能预警:不想背单词,有可能你已经被传染了. 好好的,怎么突然不想背单词了 哈佛医学 ...

随机推荐

  1. Python笔记五之正则表达式

    本文首发于公众号:Hunter后端 原文链接:Python笔记五之正则表达式 这一篇笔记介绍在 Python 里使用正则表达式. 正则表达式,Regular Expression,可用于在一个目标字符 ...

  2. Docker实践之09-高级网络配置

    目录 一.Docker网络原理及默认配置 二.Docker网络定制配置参数 三.容器访问控制原理 1.容器访问外部网络 2.容器之间访问 3.访问所有端口 4.访问指定端口 5.映射容器端口到主机端口 ...

  3. docker 发布.net core 项目(linux)

    一.准备阶段:前提:一台linux系统,安装好了Docker并启动 1.上传.netcore项目压缩文件 2.解压 注:若没有解压软件,先下载rar解压软件再安装:需注意系统是64位还是32   (下 ...

  4. [Rust] 变量的属性: 不可变(immutable), 可变(mutable), 重定义(shadowing), 常量(const), 静态(static)

    [Rust] 变量的属性: 不可变(immutable), 可变(mutable), 重定义(shadowing), 常量(const), 静态(static) 变量的可变性 在 Rust 中, 变量 ...

  5. 【应用服务 App Service】 App Service Rewrite 实例 -- 限制站点的访问

    问题描述 在Azure App Service中,当需要限制某些特殊的情况对其进行访问时候,可以通过IP限制,逻辑代码判断,或者Rewrite规则.通过IP限制则需要知道客户端访问的IP,而通过逻辑代 ...

  6. C笔记(2014-12备份)

    Video1: 1-编译器对待全局变量和局部变量的差别.全局变量分配空间是在数据区,局部变量分配在代码区. (比如局部变量 int lo_var = 2;后面的 = 2;是赋值语句,被编译器转化成机器 ...

  7. Java 异常处理(2) : 异常处理的方式二:throws + 异常类型

    1 package com.bytezero.throwable; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 impor ...

  8. CYQ.Data 支持 DaMeng 达梦数据库

    DaMeng 达梦数据库介绍: 达梦数据库(DMDB)是中国自主研发的关系型数据库管理系统,由达梦科技股份有限公司开发. 达梦数据库提供了企业级的数据库解决方案,广泛应用于金融.电信.政府.制造等行业 ...

  9. Inno setup 脚本判断 Microsoft Visual C++ Redistributable 不同版本区别

    有个需要是需要在安装包安装初始化时安装 Microsoft Visual c++ 2013 Redistributable 也就是判断软件安装前需不需要运行 vcredist_x64.exe 和 VC ...

  10. day06-IO流应用01

    Java坦克大战06 8.IO流应用01 坦克大战6.0版 增加功能: 防止敌人坦克重叠运动 记录玩家的成绩(累计击毁坦克数),存盘退出 记录当时的敌人坦克坐标,存盘退出 玩游戏时,可以选择是开新游戏 ...