AGC009D题解
前言
这是一道神仙题
我翻阅的很多分题解,包括Atcoder官方题解
都没有看懂,应该是因为我比较菜
然后我看懂了这篇(地址放在文末)
方法可能和主流略有不同 但我觉得这个办法更好理解
题面
题面大意
定义一个单独的节点为一棵Uninity 0的树。
将棵Uninity k的树全部连到一个节点上形成的树,称之为一棵Uninity k+1的树。
显然,一棵Uninity k的树,同样也是一棵Uninity k+1,k+2,k+3...的树。
现在给你一棵树,求一个最小的k使得这棵树是一棵Uninity k的树。
样例 #1
样例输入 #1
7
1 2
2 3
2 4
4 6
6 7
7 5
样例输出 #1
2
样例 #2
样例输入 #2
12
1 2
2 3
2 4
4 5
5 6
6 7
7 8
5 9
9 10
10 11
11 12
样例输出 #2
3
提示
数据范围
解法
需要知道的一些性质
- 一颗点分树的最深深度的点是级别的
证明:点分树的定义可得
点分树是通过更改原树形态使树的层数变为稳定 的一种重构树。 ——OI wiki
- 两个标号相等的点之间肯定有一个标号严格小于他们的标号的点
证明:点分树的过程里面可以证明这一点 画个图就出来了
做法
我们将每一个点的标号,记为在这个点在这颗点分树的深度
利用性质二,可以建出一颗树。我们不妨把所有的节点编号给反过来 叶子结点是0 这样我们就会将这个结论反过来,变成:
两个标号相等的点之间肯定有一个标号严格大于他们的标号的点
我们不妨设为从的子树的节点到的路径上还有多少个标号为的点没有被消灭掉,即没有严格大于的点。
注意,这里第二位只需开到级别,由性质1可以得到。
然后发现这个数组是叠加的
所以我们遍历子树以后叠加值
如果我们在一颗子树(以为根)里面找到 个点(即)
那么由性质2,我们可以得到这颗子树里面的最小可能的最大标号是,这个 我们枚举得到
现在我们找到了最大的那个值(设它为),然后我们再往上找,知道找到了一个不存在的,就是最大的,再换句话就是在往上一个就会存在不合法。
然后我们将以为根的子树的点个所有的点给清空掉,然后更新答案就行了
外话:我不知道那篇题解里面为什么要加一个s[]
数组,我删掉以后也过了
代码
# include<bits/stdc++.h>
using namespace std;
# define N 100005
# define LOG 20
# define ADD for (int _ = 0; _ < LOG; ++_)
vector<int> G[N];
int n, lim, ans, f[N][LOG];
inline void dfs (int u, int fa) {
for (int v : G[u]) {
if (v == fa) continue;
dfs (v, u);
ADD {
f[u][_] += f[v][_];;
}
}
lim = 0;
ADD {
if (f[u][LOG - 1 - _] > 1) {
lim = LOG - _;
break;
}
}
while (f[u][lim]) {
++lim;
}
ADD {
if (_ == lim) {
break;
}
f[u][_] = 0;
}
++f[u][lim];
ans = max (ans, lim);
}
signed main() {
cin >> n;
for (int i = 1, u, v; i < n; ++i) {
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs (1, 0);
cout << ans << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?