CF1437D 最小深度的树
1 CF1437D 最小深度的树
2 题目概要:
莫诺卡普有一棵树,它由 \(𝑛\) 个顶点组成,根在顶点 \(1\)。 他决定研究 \(bfs\),所以他从根开始在树上运行 \(bfs\)。 莫诺卡普玩的非常开心,结果最后他把树给弄丢了。 幸运的是,他仍然有一个顶点序列,其中顺序顶点被 \(bfs\) 算法访问。 莫诺卡普知道每个顶点只被访问了一次(因为它们被放置并从队列中提取了一次)。 此外,他还知道每个顶点的所有孩子都是按升序看待。
莫诺卡普知道有很多树(在一般情况下)有相同的访问顺序 \(𝑎\),所以他不希望恢复他的树,只要找到最小深度的树就好了。
树的深度是树的顶点的最大深度,顶点的深度是从根到它的路径中的边数。 例如,顶点 \(1\) 的深度是 \(0\),因为它是根,所有根的子节点的深度都是 \(1\)。
帮助莫诺卡普找到任何具有给定访问顺序 \(𝑎\) 和最小深度的树。
3 解题思路
这道题的题目描述很复杂,但我们可以分析一下性质。因为在 \(bfs\) 的过程中,与同一个节点的儿子节点点集 \(^1\) 中的元素都是递增的,那么在给出的\(bfs\)搜索顺序序列中递增的子序列 \(^2\) 可以看成是同一节点的儿子节点点集。在这种分配下,这棵树的层数最少,因为被认为是同一层的节点在满足 \(bfs\) 搜索规则的所有分配中最多。
然而,简单地输出 \(bfs\) 搜索顺序序列中递增的子序列的数量是错误的。这是因为当第 \(x\) 层有两个节点时,第 \(x+1\) 层就可以容纳两个递增的子序列。总结来说,当前层可以容纳的子序列个数等于上一层所容纳的所有子序列长度之和。
从上述出发,我们可以推导出本题做法。首先,我们可以将 \(bfs\) 搜索顺序序列中递增的子序列的长度提取出来。然后,我们用一个前缀数组计算长度的前缀和。每次计算,我们将当前层的所有子序列长度之和个子序列在下一层中挑选出来,计算他们的长度之和,再将总层数 $+1 $即可。
\(1\):这里的儿子节点点集意为为同一节点子节点且与该节点距离为 \(1\) 的节点的集合。
\(2\):这里的子序列意为原序列中连续的一段数字。
4 代码实现
代码(空格警告):
#include <iostream>
#include <queue>
using namespace std;
const int N = 2e5+10;
int t, n, cnt, len, l, cnt2, now;
int a[N], q[N], sum[N];
bool f[N];
int main()
{
cin >> t;
while (t--)
{
cin >> n;
cnt = 0;
len = 0;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 2; i <= n; i++)
{
if (a[i] < a[i-1])
{
q[++cnt] = len;
len = 0;
}
len++;
}
l = 1;
cnt2 = 1;
now = q[1];
for (int i = 1; i <= cnt; i++) sum[i] = sum[i-1] + q[i];
for (int i = 1; i <= cnt; i++)
{
if (i < l) continue;
l += now;
now = sum[l] - sum[i];
cnt2++;
}
cout << cnt2 << endl;
}
return 0;
}