题目链接

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)\).

代码

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cassert>
#include<vector>
#define llong long long
#define pll pair<llong,llong>
#define mkpr make_pair
using namespace std; const int N = 1<<17;
int son[N+3][2];
llong w[N+3];
vector<pll> dp[N+3];
vector<pll> aux1,aux2;
int n,en;
llong mid; void dfs(int u)
{
// printf("dfs %d\n",u);
dp[u].clear(); int ls = son[u][0],rs = son[u][1];
if(!ls)
{
dp[u].push_back(mkpr(0ll,0ll));
return;
}
dfs(ls); dfs(rs);
aux1.clear(); aux2.clear();
if(dp[rs].size())
{
int j = 0;
for(int i=0; i<dp[ls].size(); i++)
{
while(j<dp[rs].size()-1 && dp[rs][j+1].first+dp[ls][i].second+w[ls]+w[rs]<=mid) {j++;}
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]));}
}
}
if(dp[ls].size())
{
int j = 0;
for(int i=0; i<dp[rs].size(); i++)
{
while(j<dp[ls].size()-1 && dp[ls][j+1].first+dp[rs][i].second+w[ls]+w[rs]<=mid) {j++;}
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]));}
}
}
int j = 0,k = 0; llong cur = 1ll<<34;
while(j<aux1.size() || k<aux2.size())
{
if(k==aux2.size() || (j<aux1.size() && aux1[j].first<=aux2[k].first))
{
if(aux1[j].second<cur) {dp[u].push_back(aux1[j]); cur = aux1[j].second;}
j++;
}
else
{
if(aux2[k].second<cur) {dp[u].push_back(aux2[k]); cur = aux2[k].second;}
k++;
}
}
} bool check()
{
dfs(1);
if(dp[1].size()) {return true;}
else {return false;}
} int main()
{
scanf("%d",&n);
for(int i=2; i<=n; i++)
{
int u; llong x; scanf("%d%lld",&u,&x);
w[i] = x; if(son[u][0]) son[u][1] = i; else son[u][0] = i;
}
llong left = 0ll,right = 1ll<<34;
while(left<right)
{
mid = left+((right-left)>>1)
// printf("mid=%lld\n",mid);
bool ok = check();
if(ok) {right = mid;}
else {left = mid+1;}
}
printf("%lld\n",right);
return 0;
}

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. 大数据学习(2)- export、source(附带多个服务器一起启动服务器)

    linux环境中, A=1这种命名方式,变量作用域为当前线程 export命令出的变量作用域是当前进程及其子进程. 可以通过source 脚本,将脚本里面的变量放在当前进程中 附带自己写的tomcat ...

  2. VS2008提示无法打开包括文件:“afxcontrolbars.h”解决办法

    主要造成这个问题的主要原因是,在打了SP1的VS2008下创建的项目放在没有打SP1的VS2008下就会报错   解决办法: 一.给VS2008打上SP1补丁即可      此方法比较彻底,但是安装较 ...

  3. 【Javascript】 js的构造函数与原形对象的关系

    构造函数只是提供了一个创建对象的模板,它并不是对象的原形. 对象的原形是构造函数的原形,即object. ----------------------------------------------- ...

  4. Js中去除数组中重复元素的6种方法

    方法一: Array.prototype.method1 = function(){ var arr=[]; //定义一个临时数组 for(var i = 0; i < this.length; ...

  5. vue项目使用openlayers来添加地图标注,标注样式设置的简要模板

    先把代码贴出来,注释以后有时间再写(需要留意一下这里图标的引入方式,函数内相同路径无法找到图片) import sk from "../../assets/img/home/sk-activ ...

  6. 第五篇python进阶之深浅拷贝

    目录 第五篇python进阶之深浅拷贝 一.引言 1.1可变 和不可变 二.拷贝(只针对可变数据类型) 三.浅拷贝 四.深拷贝 第五篇python进阶之深浅拷贝 一.引言 1.1可变 和不可变 id不 ...

  7. arm交叉编译sudo-1.8.6p7

    1.交叉编译 # tar -xvf sudo-1.8.6p7.tar.gz # cd sudo-1.8.6p7/ # mkdir build # ./configure --prefix=/home/ ...

  8. 理解函数声明--signal函数的声明

    1.显示调用首地址为0的例程:(*(void(*)())0)() 显示调用首地址为0的例程的表达式为:(*(void(*)())0)() 分两步分析: 假定变量fp是一个函数指针,调用方法如下:(*f ...

  9. vim复制到剪切板

    作者:whinc链接:https://www.zhihu.com/question/19863631/answer/89354508来源:知乎 转载文章 Vim 中的复制.删除的内容都会被存放到默认( ...

  10. 重新编译mysqldump,使mysqldump具有进度输出功能

    重新编译mysql,使mysqldump具有进度输出功能 安装编译过程所必须的依赖包和环境 yum install -y gcc cmake boost boost-build boost-devel ...