1 条题解
-
0
自动搬运
来自洛谷,原作者为

dodo
**搬运于
2025-08-24 22:01:35,当前版本为作者最后更新于2019-03-28 15:23:04,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
楼上两篇题解写的有一点点复杂,有map还写了离散化……
差分固然是一种理解方式,但其实有一种更好的理解方法和更简洁的代码。
那么现在我就来讲一讲
题意简述
文字语言:求树上最大权值随祖孙关系不降的点集大小
数学语言:求 使得
为了方便描述,我们定义这种集合为“树上LIS”。
题解
考虑采用数学归纳法
类似处理序列LIS问题,对于每一个点 使用multiset维护一个集合 满足以下性质
-
表示在 的子树中选择 个点组成的所有树上LIS中,级别值 最小值最大的那一个。
-
以 为根节点的 (表示集合 的大小)(该性质可由上述性质发现)
对于任意一个叶子节点 , 显然只含有 ,满足树上LIS性质。
再考虑不是叶子节点的
假设点 的所有孩子 的 已经满足求出并满足上述性质,我们应该如何求出 的 呢?
首先,显然 的所有孩子不会相互影响,要从以 为根节点的子树(除 )中选出大小为 的树上LIS,可以直接贪心地选所有孩子集合中最大的 个,于是只需将全部 取并集并排序即可,于是可以直接将孩子们的 集合全部启发式合并丢入 的multiset ,记
现在我们考虑将 加入 集合并使集合满足性质
我们直接在multiset上二分出第一个 满足 那么我们将 接在 前显然是最优方案,此时 就可以被 替换,那么现在的集合就是我们要求的 ,并且满足树上LIS性质。
按照这样的方式在树上dfs即可求出 ,此时答案即为 。
复杂度证明
该算法的复杂度为
考虑同样采用数学归纳法
记 表示处理出 的时间复杂度,表示 的子树大小
我们需要证明
对于任意一个叶子节点 ,,此时只需在multiset中插入 复杂度为 ,满足
再考虑不是叶子节点的
假设点 的所有孩子 的
那么
因为子孙们包含的节点个数
所以
启发式合并的复杂度为 ,使用multiset维护加一个log,
所以与 同阶
证毕。
代码
#include <bits/stdc++.h> using namespace std; const int N = 2e5 + 5; multiset<int> f[N]; multiset<int>::iterator it; int n, w[N], ans; int h[N], to[N], nxt[N], t; bool comp(int x, int y) { return w[x] < w[y]; } void add(int u, int v) { to[++t] = v, nxt[t] = h[u], h[u] = t; } void merge(int u, int v) { if(f[u].size() < f[v].size()) swap(f[u], f[v]); for(it = f[v].begin(); it != f[v].end(); ++it) f[u].insert(*it); } void dfs(int u) { for(int i = h[u]; i; i = nxt[i]) dfs(to[i]), merge(u, to[i]); f[u].insert(w[u]); it = f[u].lower_bound(w[u]); if(it != f[u].begin()) f[u].erase(--it); } int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%d", &w[i]); for(int i = 2; i <= n; ++i) { int f; scanf("%d", &f); add(f, i); } dfs(1); printf("%d", f[1].size()); } -
- 1
信息
- ID
- 3562
- 时间
- 1000ms
- 内存
- 250MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者