题目链接

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. ubuntu14.04 x86编译upx 3.92 及so加固

    的参考文章: http://www.cnblogs.com/fishou/p/4202061.html 1.download upx和所依赖的组件 upx3.:https://www.pysol.or ...

  2. 你真的知道em和rem的区别吗?

    前言 em 和 rem 都是相对单位,在使用时由浏览器转换为像素值,具体取决于您的设计中的字体大小设置. 如果你使用值 1em 或 1rem,它可以被浏览器解析成 从16px 到 160px 或其他任 ...

  3. 4 java 笔记

    1 javadoc工具默认只处理以public,protected修饰的类,接口,方法,成员变量,构造器和内部类之前的文档注释 2 文档注释以斜线后紧跟两个星号开始/**,以星号后紧跟一个斜线结束*/ ...

  4. 测试使用Timer定时调用http接口

    转自:https://blog.csdn.net/qq_36004521/article/details/80101881

  5. <(* ̄▽ ̄*)/低碳生活管理系统

    首次在博客园发布文章,都是个人理解,有很多不足,多多包涵. 下面是对做过的作业低碳生活管理系统的剖析: 一:(建类) 二:(在Program[或自己命名的类]里调用SQL命名空间) 三:(Main方法 ...

  6. ubuntu python3.5升级3.6后打不开终端的解决办法

    ubuntu python3.5升级3.6后打不开终端了. 解决办法如下: 1.Ctrl+Alt+F1进入命令行终端,我的电脑按Ctrl+Alt+F1没反应,按住Ctrl+Alt然后从F1到F5一个个 ...

  7. 公司最喜欢问的Java集合类

    java.util包中包含了一系列重要的集合类,而对于集合类,主要需要掌握的就是它的内部结构,以及遍历集合的迭代模式. 接口:Collection Collection是最基本的集合接口,一个Coll ...

  8. 7、TortoiseSVN

    7.TortoiseSVN TortoiseSVN图标介绍: 目录空白处右键→TortoiseSVN→Settings 7.1独立将工程上传到服务器的思路 12.2针对archetype-catalo ...

  9. Delphi 字段的操作

    樊伟胜

  10. STM32定义变量位于指定的SRAM地址

    1.定义一个数组比如value[],让数组的首地址指向特定的SRAM地址,比如0x20000100 1)__align(8) uint8_t value[20] __attribute__((at(0 ...