题目链接

https://atcoder.jp/contests/agc007/tasks/agc007_e

题解

首先有个很朴素的想法是,二分答案\(mid\)后使用可行性DP, 设\(dp[u][x][y]\)表示\(u\)子树内是否可以找到一条路径,在经过第一个叶子前路程是\(x\), 经过最后一个叶子前路程是\(y\).

这个DP显然做了很多无用功,比如我们发现完全可以只记录true的状态\((x,y)\),进一步发现如果合法状态\((x,y)\)存在另一合法状态\((x',y')\)满足\(x'\le x,y'<\le y\), 那么就没有必要存储\((x,y)\)了。于是我们按\(x\)递增的顺序存储\((x,y)\),那么\(y\)一定是递减的。

这样简化之后,我们发现一个神奇的性质: 设\(S_u\)为\(u\)记录的集合,\(i\)和\(j\)为儿子,那么\(|S_u|\le 2\min(|S_i|,|S_j|)\). 这是因为\(x\)和\(y\)的取值都各有\(\min(|S_i|,|S_j|)\)种。

考虑合并的过程: 假设路径的开头在\(i\)内,那么我们需要找到\((x_1,y_1)\in S_i, (x_2,y_2)\in S_j\), 若\(y_1+v_i+v_j+x_2\le mid\), 则把\((x_1+v_i,y_2+w_j)\)加入\(S_u\). 这个显然可以用双指针优化. 路径的开头在\(j\)内也同理。

类似启发式合并可分析复杂度。算上二分总复杂度\(O(n\log^2n)\).

代码

  1. #include<cstdio>
  2. #include<cstdlib>
  3. #include<iostream>
  4. #include<cassert>
  5. #include<vector>
  6. #define llong long long
  7. #define pll pair<llong,llong>
  8. #define mkpr make_pair
  9. using namespace std;
  10. const int N = 1<<17;
  11. int son[N+3][2];
  12. llong w[N+3];
  13. vector<pll> dp[N+3];
  14. vector<pll> aux1,aux2;
  15. int n,en;
  16. llong mid;
  17. void dfs(int u)
  18. {
  19. // printf("dfs %d\n",u);
  20. dp[u].clear(); int ls = son[u][0],rs = son[u][1];
  21. if(!ls)
  22. {
  23. dp[u].push_back(mkpr(0ll,0ll));
  24. return;
  25. }
  26. dfs(ls); dfs(rs);
  27. aux1.clear(); aux2.clear();
  28. if(dp[rs].size())
  29. {
  30. int j = 0;
  31. for(int i=0; i<dp[ls].size(); i++)
  32. {
  33. while(j<dp[rs].size()-1 && dp[rs][j+1].first+dp[ls][i].second+w[ls]+w[rs]<=mid) {j++;}
  34. if(j<dp[rs].size() && dp[rs][j].first+dp[ls][i].second+w[ls]+w[rs]<=mid) {aux1.push_back(mkpr(dp[ls][i].first+w[ls],dp[rs][j].second+w[rs]));}
  35. }
  36. }
  37. if(dp[ls].size())
  38. {
  39. int j = 0;
  40. for(int i=0; i<dp[rs].size(); i++)
  41. {
  42. while(j<dp[ls].size()-1 && dp[ls][j+1].first+dp[rs][i].second+w[ls]+w[rs]<=mid) {j++;}
  43. if(j<dp[ls].size() && dp[ls][j].first+dp[rs][i].second+w[ls]+w[rs]<=mid) {aux2.push_back(mkpr(dp[rs][i].first+w[rs],dp[ls][j].second+w[ls]));}
  44. }
  45. }
  46. int j = 0,k = 0; llong cur = 1ll<<34;
  47. while(j<aux1.size() || k<aux2.size())
  48. {
  49. if(k==aux2.size() || (j<aux1.size() && aux1[j].first<=aux2[k].first))
  50. {
  51. if(aux1[j].second<cur) {dp[u].push_back(aux1[j]); cur = aux1[j].second;}
  52. j++;
  53. }
  54. else
  55. {
  56. if(aux2[k].second<cur) {dp[u].push_back(aux2[k]); cur = aux2[k].second;}
  57. k++;
  58. }
  59. }
  60. }
  61. bool check()
  62. {
  63. dfs(1);
  64. if(dp[1].size()) {return true;}
  65. else {return false;}
  66. }
  67. int main()
  68. {
  69. scanf("%d",&n);
  70. for(int i=2; i<=n; i++)
  71. {
  72. int u; llong x; scanf("%d%lld",&u,&x);
  73. w[i] = x; if(son[u][0]) son[u][1] = i; else son[u][0] = i;
  74. }
  75. llong left = 0ll,right = 1ll<<34;
  76. while(left<right)
  77. {
  78. mid = left+((right-left)>>1)
  79. // printf("mid=%lld\n",mid);
  80. bool ok = check();
  81. if(ok) {right = mid;}
  82. else {left = mid+1;}
  83. }
  84. printf("%lld\n",right);
  85. return 0;
  86. }

AtCoder AGC007E Shik and Travel (二分、DP、启发式合并)的更多相关文章

  1. AGC007E Shik and Travel 解题报告

    AGC007E Shik and Travel 题目大意:\(n\) 个点的二叉树,每个点要么两个儿子,要么没有儿子,每条边有边权. 你从 \(1\) 号节点出发,走到一个叶子节点.然后每一天,你可以 ...

  2. ARC 086 E - Smuggling Marbles(dp + 启发式合并)

    题意 Sunke 有一棵 \(N + 1\) 个点的树,其中 \(0\) 为根,每个点上有 \(0\) 或 \(1\) 个石子, Sunke 会不停的进行如下操作直至整棵树没有石子 : 把 \(0\) ...

  3. BZOJ4919 [Lydsy1706月赛]大根堆 【dp + 启发式合并】

    题目链接 BZOJ4919 题解 链上的\(LIS\)维护一个数组\(f[i]\)表示长度为\(i\)的\(LIS\)最小的结尾大小 我们可以用\(multiset\)来维护这个数组,子树互不影响,启 ...

  4. [AGC007E] Shik and Travel

    题目 给定一棵n节点的 以1为根的 满二叉树 (每个非叶子节点恰好有两个儿子)n−1 条边. 第ii条边连接 i+1号点 和 ai, 经过代价为vi设这棵树有m个叶子节点定义一次合法的旅行为:(1) ...

  5. 【AtCoder Grand Contest 007E】Shik and Travel [Dfs][二分答案]

    Shik and Travel Time Limit: 50 Sec  Memory Limit: 512 MB Description 给定一棵n个点的树,保证一个点出度为2/0. 遍历一遍,要求每 ...

  6. [AT2172] [agc007_e] Shik and Travel

    题目链接 AtCoder:https://agc007.contest.atcoder.jp/tasks/agc007_e 洛谷:https://www.luogu.org/problemnew/sh ...

  7. P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并

    LINK:Druzyny 这题研究了一下午 终于搞懂了. \(n^2\)的dp很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...

  8. [多校 NOIP 联合模拟 20201130 T4] ZZH 的旅行(斜率优化dp,启发式合并,平衡树)

    题面 题目背景 因为出题人天天被 ZZH(Zou ZHen) 吊打,所以这场比赛的题目中出现了 ZZH . 简要题面 数据范围 题解 (笔者写两个log的平衡树和启发式合并卡过的,不足为奇) 首先,很 ...

  9. 二分+DP HDU 3433 A Task Process

    HDU 3433 A Task Process Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/ ...

随机推荐

  1. Lua的API函数

    1. 基础库 我们在整个教程中使用了各种主题下的基本库. 下表提供了相关页面的链接,并列出了本Lua教程各部分所涵盖的功能. 编号 库/方法 作用 1 错误处理 包括错误处理函数,如断言, 错误,如L ...

  2. Scala学习三——数组相关操作

    一.若长度固定则使用Array,若长度可能有变化则使用ArrayBuffer 固定长度数组: 如val nums=new Array[Int](10) //10个整型数组,所有元素初始化为0; val ...

  3. ASP.NET Core如何限制请求频率

    原文:ASP.NET Core如何限制请求频率 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.ne ...

  4. qq快捷打开聊天窗口的代码

    pc代码: http://wpa.qq.com/msgrd?v=3&uin=8423291&site=qq&menu=yes

  5. vue项目中实现图片懒加载的方法

    对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载.这样子对于页面加载性能上会有很大的提升,也提高了用户体验. 实 ...

  6. fastadmin 中 selectpage.js位置

    备注: //特殊字段处理 因为接收到input中的属性名会被转成小写所以增加了一对键值 keyField: 'primarykey' $.each({data: 'source', keyField: ...

  7. 在不同的Linux发行版上安装TFTP Server

    http://www.davidsudjiman.info/2006/03/27/installing-and-setting-tftpd-in-ubuntu/ http://www.cybercit ...

  8. Oracle【三表的联合查询】

    ,'北京','彰显大气'); ,'上海','繁华都市'); ,'广州','凸显舒适'); ,'深圳','年轻气氛'); ,'北上广深','不相信眼泪'); commit; ; ; ; ; ; 员工信息 ...

  9. deep_learning_凹凸函数

    什么是凸函数及如何判断一个函数是否是凸函数 t元j 一.什么是凸函数 对于一元函数f(xf(x),如果对于任意tϵ[0,1]tϵ[0,1]均满足:f(tx1+(1−t)x2)≤tf(x1)+(1−t) ...

  10. IPC之util.c源码解读

    // SPDX-License-Identifier: GPL-2.0 /* * linux/ipc/util.c * Copyright (C) 1992 Krishna Balasubramani ...