Solution Set - 咋提玄坛 | 说无可说

说无可说。

I have nothing to say.

Mi havas nenion por diri.

Je n'ai rien à dire.

upd:之前忘加 ne,现在补上了。

reupd:非正式的话可以省掉 ne 的。

说无可说

Link。

我说,我去,暴力 dp 一次就是 \(O(|S| ^ 2)\) 的了,直接起飞!

题目说,我只要求相似度为 \(1 \sim 8\) 的字符串对数,there must be a reason。

我说,来可以 dfs,太奇啦!但是这要怎么搜啊?那么其实类似 dp 那么记录端点,,每次 dfs 转移下一个状态就是先暴力匹配,找到第一个依次推下去不相等的字符,从这里开始按照 dp 那样转移即可,具体可见代码。为了提升效率加一条最优性剪枝,因为当前答案一定不小于目前搜索的答案加上需要处理的字符串的长度之差(必须用加减字符达成同样长度)。\(O(n ^ 2)\) 暴力枚举一下搜就好了。

我说,有一个小细节,string 的长度是 size_t 类型的,如果减的话可能会爆炸,要强转 int。

我说,这里算的是起点,dfs(x, y, step) 表示字符串 \(s[x \dots \operatorname{end}]\)\(t[y \dots \operatorname{end}]\),目前耗费了 \(step\) 步,而 dp 算的是一端的终点,本质相同。

namespace liuzimingc {
const int N = 205;
#define endl '\n'int n, i, j, res, ans[10];
string s[N];void dfs(int x, int y, int step) {if (step + abs((int)(s[i].length() - x) - (int)(s[j].length() - y)) >= res) return;while (x < s[i].length() && y < s[j].length() && s[i][x] == s[j][y]) x++, y++;if (x >= s[i].length() && y >= s[j].length()) {res = step;return;}if (x < s[i].length()) dfs(x + 1, y, step + 1);if (y < s[j].length()) dfs(x, y + 1, step + 1);if (x < s[i].length() && y < s[j].length()) dfs(x + 1, y + 1, step + 1);
}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];for (i = 1; i <= n; i++)for (j = i + 1; j <= n; j++) {res = 9;dfs(0, 0, 0);ans[res]++;}for (int i = 1; i <= 8; i++) cout << ans[i] << " \n"[i == 8];return 0;
}
#undef int
} // namespace liuzimingc

我说,我不会傻逼爆搜。说无可说。

消防

首先,枢纽一定在树的直径上。

证明咕咕。

那么我们随便拉出来一条直径,那么所有点到路径的距离就分成两种情况:

  • 点在直径上。只能是路径的两个端点。可以用双指针 \(O(n)\) 维护长度和不大于 \(s\) 的路径(显然越大越好)。显然一定是直径两端离路径最远,\(O(1)\) 算就行。
  • 点不在直径上。从直径上的点对其他点 dfs 一下就好了,每个点只会遍历一次所以是 \(O(n)\) 的。

然后综合两种情况就做完了!

namespace liuzimingc {
const int N = 3e5 + 5;
#define endl '\n'int n, s, dep[N], x, fa[N], ans = 0x3f3f3f3f;
vector<pair<int, int>> e[N];
bool vis[N];void dfs(int u, int f) {fa[u] = f;if (dep[u] > dep[x]) x = u;for (const auto &i : e[u]) {int v = i.first, w = i.second;if (v == f || vis[v]) continue;dep[v] = dep[u] + w;dfs(v, u);}
} // dep 得到从某一点开始到所有点的距离 int main() {ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);cin >> n >> s;for (int i = 1; i < n; i++) {int u, v, w;cin >> u >> v >> w;e[u].push_back(make_pair(v, w));e[v].push_back(make_pair(u, w));}x = 1;dfs(1, 0);dep[x] = 0;dfs(x, 0); // 两次都找距离最远的点,求直径int begin = x; // 这条直径的最底端(抽象说法),fa[x] 就是跳上去for (int i = begin, j = begin; i; i = fa[i]) {while (dep[j] - dep[i] > s) j = fa[j];ans = min(ans, max(dep[i], dep[begin] - dep[j])); }for (int i = begin; i; i = fa[i]) vis[i] = true;for (int i = begin; i; i = fa[i]) {x = i;dep[i] = 0;dfs(i, fa[i]);}for (int i = 1; i <= n; i++) ans = max(ans, dep[i]); // 两种情况都要满足cout << ans << endl;return 0;
}
#undef int
} // namespace liuzimingc

以及顺便学到了两次 bfs / dfs 求直径,以前不知道。。。

小凸玩密室

每一次花费和上一次选的点有关,直接记录显然死翘翘,感觉很辣手,不好操作。

在点灯的过程中,要保证任意时刻所有被点亮的灯泡必须连通,在点亮一个灯泡后必须先点亮其子树所有灯泡才能点亮其他灯泡。

因为是二叉树,相当于点亮一个灯泡之后只会要么点左儿子要么点右儿子(如果有),直到其子树被点完。然后考虑整体的过程,点完一颗子树后,肯定需要回退到那个点的祖先上去(不然你还能干嘛),然后再选另一颗子树(如果有),如此重复操作。那么我们根据这个来定义状态,\(f_{i, j, 0}\) 表示从 \(i\) 本身出发并点亮其除去 \(i\) 的子树到 \(i\)\(j\) 级祖先并点亮它的最小代价,\(f_{i, j, 1}\) 表示从从 \(i\) 本身出发并点亮其除去 \(i\) 的子树到 \(i\)\(j\) 级祖先的另一个儿子并点亮它的最小代价。注意定义 \(i\) 这里没有包含,而点儿子、祖先包含了,是为了良好的顺接起来整个过程,否则如果都包含会出现重复。

那么定义完之后其实状态转移就比较 OK 了。比如若 \(i\) 有左右儿子,计算 \(f_{i, j, 1}\),则分成两种情况:先点左子树或先点右子树。这里又以先点左子树为例,那么先点左儿子,然后点左儿子的子树除去左儿子并回到原节点,然后点右儿子,最后点右儿子的子树除去右儿子并回到 \(i\)\(j\) 级祖先的另一个儿子(符合定义),即 \(dis(i, ls_i) \times a_{ls_i} + f_{ls_i, 1, 1} + f_{rs_i, j + 1, 1}\)。其它的同理。最后求完了之后依次枚举起点模拟上面那个整体的操作即可。

为了方便实现定义了一个 \(dis_{i, j}\) 表示 \(i\)\(i\)\(j\) 级祖先的路径长度,\(bro(i, j)\) 表示 \(i\)\(j\) 级祖先的另一个儿子。

namespace liuzimingc {
const int N = 2e5 + 5; 
#define endl '\n'
#define int long longint n, a[N], f[N][21][2], b[N], dis[N][21], ans = 1e18; // follow gm's instructions d'abord, et ensuite...
vector<pair<int, int>> e[N];int bro(int x, int dep) {return (x >> dep - 1) ^ 1;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr); cout.tie(nullptr);cin >> n;for (int i = 1; i <= n; i++) cin >> a[i];for (int i = 1; i < n; i++) cin >> dis[i + 1][1];for (int i = 2; i <= 20; i++)for (int j = 1; j <= n; j++)dis[j][i] = dis[j][i - 1] + dis[j >> i - 1][1];for (int i = n; i; i--)if ((i << 1) <= n) {if ((i << 1 | 1) <= n) {for (int j = 1; i >> j - 1; j++)f[i][j][1] = min(dis[i << 1][1] * a[i << 1] + f[i << 1][1][1] + f[i << 1 | 1][j + 1][1], dis[i << 1 | 1][1] * a[i << 1 | 1] + f[i << 1 | 1][1][1] + f[i << 1][j + 1][1]);}else {for (int j = 1; i >> j - 1; j++)f[i][j][1] = dis[i << 1][1] * a[i << 1] + f[i << 1][j + 1][1];}}else {for (int j = 1; i >> j - 1; j++)f[i][j][1] = (dis[i][j] + dis[bro(i, j)][1]) * a[bro(i, j)];}for (int i = n; i; i--)if ((i << 1) <= n) {if ((i << 1 | 1) <= n) {for (int j = 1; i >> j - 1; j++)f[i][j][0] = min(dis[i << 1][1] * a[i << 1] + f[i << 1][1][1] + f[i << 1 | 1][j + 1][0], dis[i << 1 | 1][1] * a[i << 1 | 1] + f[i << 1 | 1][1][1] + f[i << 1][j + 1][0]);}else {for (int j = 1; i >> j - 1; j++)f[i][j][0] = dis[i << 1][1] * a[i << 1] + f[i << 1][j + 1][0];}}else {for (int j = 1; i >> j - 1; j++)f[i][j][0] = dis[i][j] * a[i >> j];}for (int i = 1; i <= n; i++) {int sum = 0;for (int j = 1; i >> j; j++)if (bro(i, j) <= n) sum += dis[bro(i, j)][1] * a[bro(i, j)] + f[bro(i, j)][2][0];else sum += dis[i >> j][1] * a[i >> j + 1];sum += f[i][1][0];ans = min(ans, sum);}cout << ans << endl;return 0;
}
#undef int
} // namespace liuzimingc

龙门对决

咕咕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/796241.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

dc-4

靶机下载地址:dc-4 找靶机 nmap -sV 192.168.6.0/24查看一下端口服务很明显就只是一个简单的登录框 我们爆破一下 最终得到用户名:admin 密码:happy 进去看看怎么个事很明显是一个选择既定的命令进行命令执行 我们可以抓包然后即可使用我们自己设计的命令直接拿shell然后再实现…

java方法:什么是方法?

java方法是语句的集合,它们在一起执行一个功能:方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 例如:即 ______()是方法 设计方法的原则:方法的本意时功能块,就是实现某个功能块,就是实现某个功能的语句块的集合,所以…

pediatrics_llm_qa:儿科问诊小模型

项目简介 本项目开源了基于儿科医疗指令微调的问诊模型:pediatrics_llm_qa(GitHub - jiangnanboy/pediatrics_llm_qa),目前模型的主要功能如下:智能问诊:问诊后给出诊断结果和建议。更新[2024/09/11] 开源了基于Qwen2-1.5B-instruct lora指令微调的儿科问诊模型开源模型模型…

WPF 已知问题 包含 NaN 的 Geometry 几何可能导致渲染层抛出 UCEERR_RENDERTHREADFAILURE 异常

本文记录一个 WPF 已知问题,当传入到渲染的 Geometry 几何里面包含了 NaN 数值,将可能让应用程序收到从渲染层抛上来的 UCEERR_RENDERTHREADFAILURE 异常,且此异常缺乏必要信息,比较难定位到具体错误逻辑此问题是小伙伴报告给我的,详细请看 https://github.com/dotnet/wpf…

WPF 尝试使用 WinML 做一个简单的手写数字识别应用

最近我看了微软的 AI 训练营之后,似乎有点了解 Windows Machine Learning 和 DirectML 的概念,于是我尝试实践一下,用 WPF 写一个简单的触摸手写输入的画板,再使用大佬训练好的 mnist.onnx 模型,对接 WinML 实现一个简单的手写数字识别应用最近我看了微软的 AI 训练营之后…

VisualStudio 2022 找不到内存 反汇编 寄存器调试工具

本文将告诉大家如何解决在 VisualStudio 2022 的 调试-窗口 里面找不到内存、 反汇编、 寄存器这三个调试工具的问题找不到的原因是没有启用地址级调试 只需要在“工具”(或“调试”)>“选项”>“调试”中选择“启用地址级调试” 然后进行调试即可看到开启之后,即可在…

【TS】TypeScript基础详解【一】

Javascript 类型缺陷 类型引发的问题 在编程开发中,有一个共识:错误越早发现,就越容易解决。 例如:能在代码编写时发现错误,就不要等到代码编译时才发现(这也是IDE的优势之一)。 能在代码编译时发现错误,就不要在代码运行时才发现(类型检测可以帮助我们在这 方面做得很好…

【OpenFeign 】OpenFeign 的常用配置

1 前言 上节我们看了下 OpenFeign 里的重试,在从源码的角度看它的执行原理的时候,又意外的遇到了一个【OpenFeign 】OpenFeign 下未开启重试,服务却被调用了两次 的问题的分析,后面我们又看了重试器的入场和执行时机,那么本节我们看看 OpenFeign 的一些常用配置,以及全局…

Splay 浅谈

Splay 树 定义 Splay 树是一个二叉平衡搜索树,它可以通过 Splay 操作 将一个结点旋转至根结点或者一个给定的结点的下一层,使得整棵树仍然满足二叉搜索树的性质。 Splay 树可以在均摊 \(O(\log n)\) 的时间内完成查找、插入、查询、删除等操作。二叉搜索树的定义:空树是一个…

从kmp到AC自动机

知道kmp的请跳过这一段 找到最清晰的解析 kmp 我看了约114514个解析才搞懂 如何求next 首先,next[i]本应表示0~i的字符串的最长相同前缀后缀的长度。 不过为了方便匹配,实际可以存最长相同前缀后缀时前缀最后一个的地址 听起来好绕 那这么说吧: 例如串 abaabaabaab next[0]=…

LinkedHashMap原理详解—从LRU缓存机制说起

写在前面 从一道Leetcode题目说起 首先,来看一下Leetcode里面的一道经典题目:146.LRU缓存机制,题目描述如下:请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类:LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 in…

微积分快速入门1部分:直觉

1 一分钟微积分:X射线和延时视觉 我们通常只看到图形、公式和情况的表面价值。微积分为我们提供了两种深入挖掘的超能力:X射线能看到图案中隐藏的部分。你不仅能看到树,还能知道它是由年轮组成的,在我们说话的同时,另一个年轮也在生长。延时视觉你能看到物体未来的运行轨迹…