Description

给定一棵 \(n\) 个节点的树,点有点权,将树的节点划分成多个集合,满足集合的并集是树的点集,最小化每个集合最大点权之和。

Limitation

\(1~\leq~n~\leq~2~\times~10^5,~1~\leq~M~\leq~10^9\)

其中 \(M\) 表示树的最大点权。

Subtasks:

对于前 \(45\%\) 的数据, \(n~\leq~16\)

另有 \(15\%\) 的数据,保证树的形态是一条链,但是 \(1\) 号节点不一定是根节点

另有 \(15\%\) 的数据,\(n~\leq~2000\)

另有 \(25\%\) 的数据无特殊限制。

Solution

联考里唯一会做的送温暖题。

考虑前 \(45\%\) 可以直接状压

考虑一条链的数据,显然根节点的同一方向的后代不能放到集合中,于是显然需要将两个方向的后代合并。考虑其中一条链的最大权值的点显然应该选择另一侧最大的合并。证明上可以考虑调整法,如果选择另一个点的话对答案的贡献不会更优。

于是将两条链分别排序,最后加上根节点的贡献即可。

考虑将这个结论推广到一般树上也是成立的。证明:对拍了半小时没有挂

那么考虑 \(n~\leq~2000\) 的点,先按照点权排序,然后枚举没有被选进集合的点,暴力在树上将子树和到根节点的链都打上时间戳,然后向后枚举所有的点,如果一个点没有被选择且没有被打上当前时间戳,则将其选入集合,同时暴力染色,打上相同的时间戳。

考虑这样做的时间复杂度:枚举每个点和集合中的其他点是 \(O(n^2)\) 的,对于每个点,会在树上 \(O(n)\) 暴力染色一次,查询是否染色是 \(O(1)\) 的,由于上述两个操作的次数多都是 \(O(n)\) 的,于是染色和查询的总复杂度是 \(O(n^2)\) 的,加上枚举的复杂度,总复杂度 \(O(n^2)\)。

考虑剩下的部分,发现选出的集合个数显然是 \(O(maxdepth)\) 的,同时每个集合中非最大权值是无需维护的,合并两个子树可以使用同样的方法贪心。于是对每一棵子树开一个 std::priority_queue,维护该子树内选择的每个集合的最大值。考虑到发现每个对树进行长链剖分,将非长链的元素合并到长链上,由于每合并一次会有一个数被删掉,会删 \(O(n)\) 个数,于是总复杂度 \(O(n \log n)\)。

注意长链剖分在向上合并长链信息的时候只能交换两个节点的头指针,如果暴力枚举子节点的元素插入父节点的话复杂度是不对的,例如:考虑一条链的情况,会被卡到 \(O(n^2)\)。

Code

  1. #include <cstdio>
  2. #include <queue>
  3. #include <vector>
  4. #ifdef ONLINE_JUDGE
  5. #define freopen(a, b, c)
  6. #endif
  7. typedef long long int ll;
  8. namespace IPT {
  9. const int L = 1000000;
  10. char buf[L], *front=buf, *end=buf;
  11. char GetChar() {
  12. if (front == end) {
  13. end = buf + fread(front = buf, 1, L, stdin);
  14. if (front == end) return -1;
  15. }
  16. return *(front++);
  17. }
  18. }
  19. template <typename T>
  20. inline void qr(T &x) {
  21. char ch = IPT::GetChar(), lst = ' ';
  22. while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
  23. while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
  24. if (lst == '-') x = -x;
  25. }
  26. namespace OPT {
  27. char buf[120];
  28. }
  29. template <typename T>
  30. inline void qw(T x, const char aft, const bool pt) {
  31. if (x < 0) {x = -x, putchar('-');}
  32. int top = 0;
  33. do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
  34. while (top) putchar(OPT::buf[top--]);
  35. if (pt) putchar(aft);
  36. }
  37. const int maxn = 200005;
  38. int n;
  39. ll ans;
  40. int fa[maxn], MU[maxn];
  41. std::vector<int>son[maxn];
  42. std::priority_queue<int>Q[maxn];
  43. void dfs(const int u);
  44. void merge(std::priority_queue<int> &u, std::priority_queue<int> &v);
  45. int main() {
  46. freopen("1.in", "r", stdin);
  47. qr(n);
  48. for (int i = 1; i <= n; ++i) qr(MU[i]);
  49. for (int i = 2; i <= n; ++i) {
  50. qr(fa[i]);
  51. son[fa[i]].push_back(i);
  52. }
  53. dfs(1);
  54. while (!Q[1].empty()) {
  55. ans += Q[1].top(); Q[1].pop();
  56. }
  57. qw(ans, '\n', true);
  58. return 0;
  59. }
  60. void dfs(const int u) {
  61. int wson = 0;
  62. for (auto v : son[u]) {
  63. dfs(v);
  64. if (Q[wson].size() < Q[v].size()) wson = v;
  65. }
  66. for (auto v : son[u]) if (v != wson) {
  67. merge(Q[wson], Q[v]);
  68. }
  69. Q[u].swap(Q[wson]);
  70. Q[u].push(MU[u]);
  71. }
  72. void merge(std::priority_queue<int> &u, std::priority_queue<int> &v) {
  73. static int tmp[maxn];
  74. int cnt = 0;
  75. while (!v.empty()) {
  76. tmp[++cnt] = std::max(u.top(), v.top());
  77. u.pop(); v.pop();
  78. }
  79. while (cnt) u.push(tmp[cnt--]);
  80. }

Summary

注意长链剖分在向上合并长链信息的时候只能交换两个节点的头指针,如果暴力枚举子节点的元素插入父节点的话复杂度是不对的,例如:考虑一条链的情况,会被卡到 \(O(n^2)\)。

【堆的启发式合并】【P5290】[十二省联考2019]春节十二响的更多相关文章

  1. P5290 [十二省联考2019]春节十二响(堆+启发式合并)

    P5290 [十二省联考2019]春节十二响 从特殊到一般 我们先看链的情况. 我们把点$1$左右的两条子链分别扔入堆里 每次取出两个堆的最大值,把答案累加上更大的那个(另一堆为空则直接加上去). 那 ...

  2. P5290 [十二省联考2019]春节十二响

    题目地址:P5290 [十二省联考2019]春节十二响 骗分方法 如果你实在一点思路也没有,暴力都不会打,那么请考虑一下骗分. 方法一 输出所有 \(M\) 的和. 期望得分:0分. 实际还有5分 方 ...

  3. luogu P5290 [十二省联考2019]春节十二响 优先队列_启发式合并

    思维难度不大,在考上上写的启发式合并写错了,只拿了 60 pts,好难过QAQ 没什么太难的,在考场上想出链的部分分之后很容易就能想到正解.没错,就是非常短的启发式合并.注意一下,写的要漂亮一点,否则 ...

  4. LuoguP5290 [十二省联考2019]春节十二响 | 启发式合并

    还有33天就要高考了,我在干啥-- 题目概述 一棵有根树,每个节点有权值. 要求把所有节点分成组,具有祖先-后代关系的两个节点不能被分到同一组. 每一组的代价是所包含的节点的最大权值,最小化所有组的代 ...

  5. Luogu5290 十二省联考2019春节十二响(贪心+启发式合并)

    考虑链的做法,显然将两部分各自从大到小排序后逐位取max即可,最后将根计入.猜想树上做法相同,即按上述方式逐个合并子树,最后加入根.用multiset启发式合并即可维护.因为每次合并后较小集合会消失, ...

  6. Luogu P5290 [十二省联考2019]春节十二响

    这题是最近看到的今年省选题中最良心的一道了吧 看题+想题+写题都可以在0.5h内解决,送分含义明显啊 首先理解了题意后我们很快就能发现两个点如果要被分在一段那么必须在它们的祖先处合并 首先我们考虑下二 ...

  7. 【题解】Luogu P5290 [十二省联考2019]春节十二响

    原题传送门 每个点维护一个堆,表示这个点及其子树所需的每段内存的空间 搜索时从下向上做启发式合并堆中信息,最后根节点堆中所有内存空间之和就是答案 #include <bits/stdc++.h& ...

  8. Luogu P5290 / LOJ3052 【[十二省联考2019]春节十二响】

    联考Day2T2...多亏有这题...让我水了85精准翻盘进了A队... 题目大意: 挺简单的就不说了吧...(这怎么简述啊) 题目思路: 看到题的时候想了半天,不知道怎么搞.把样例画到演草纸上之后又 ...

  9. [LOJ3052] [十二省联考 2019] 春节十二响

    题目链接 LOJ:https://loj.ac/problem/3052 洛谷:https://www.luogu.org/problemnew/show/P5290 BZOJ:https://www ...

随机推荐

  1. PHP 预定义变量

    1.$_SERVER <?php $a=$_SERVER; var_dump($a); ?> 2.$_FILES <?php if($_FILES){ echo "< ...

  2. shutil模块详解

    python常用模块目录 注意:shutil经常遇到路径需要转义一下才能执行,在字符串前面加 r转义  r" " 1.shutil常用方法 import shutil# 删除目录 ...

  3. Python 命令行解析工具 Argparse介绍

    最近在研究pathon的命令行解析工具,argparse,它是Python标准库中推荐使用的编写命令行程序的工具. 以前老是做UI程序,今天试了下命令行程序,感觉相当好,不用再花大把时间去研究界面问题 ...

  4. 读书笔记 之java编程思想

    本阶段我正在读java的编程思想这本书,这本书只是刚读了第一章的一部分,有些有些要记得所以记录下来, 我认为要记得有就是复用这样可以对对象进行增强,将一个类作为下一个类中基本类型,这样达到的服用的目的 ...

  5. HDU 5925 Coconuts 离散化

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5925 Coconuts Time Limit: 9000/4500 MS (Java/Others) ...

  6. 结对作业(web)

    作业源代码地址:https://git.coding.net/mal123/arithmetic.git 网页版测试地址:http://47.93.197.5:8080/mal_war_explode ...

  7. Docker的volume机制实现容器数据的持久性存储

    1:可以启动一个容器的时候,临时指定挂载的volume,这个volume会自动创建,无需在宿主机上事先创建 docker run -it -v /busybox busybox:latest 登入到b ...

  8. Sprint1回顾

    Sprint目标 此产品为适用于小学生使用的四则运算训练软件.关于第一期Sprint冲刺的目标,我们打算实现产品的以下几点的功能: •1.初始界面设计 •2.四则基本运算算法 •3.能产生随机式子 • ...

  9. servlet跳转问题

    <!-- 相对路径访问 第一个/表示服务器的根目录--> <a href="servlet/o1">访问01/src/servlet/01.java< ...

  10. cxGrid使用汇总

    1.自动行高:CellAutoHeight(单元自动高度)设置为True. procedure <AForm>.<AGridColumn>PropertiesValidate( ...