[AGC007E] Shik and Travel
题目
给定一棵n节点的 以1为根的 满二叉树 (每个非叶子节点恰好有两个儿子)
n−1 条边. 第ii条边连接 i+1号点 和 ai, 经过代价为vi
设这棵树有m个叶子节点
定义一次合法的旅行为:
(1) 旅行m+1天, 旅行从11号点出发, 最后回到11号点
(2) 第 1 ..m天, 每天 从上一天的结束点出发, 前往一个叶子节点, 然后结束这一天
(记第0天的结束点为1)
第 m+1 天, 从上一天的结束点出发, 前往1号点
(3) 旅行过程中, 每条边恰好经过两次
定义一天的花费为 : 起点到终点的边权和
定义一次旅行的代价为: 第 2 ..m天中, 花费的**最大值**
求一种最优旅行方案, 使得旅行的代价**最小**
输出最小值
2<n<131072
1≤ai<i ∀i
0≤vi≤131072
题解
观察到$vi<=131072$,我们可以对答案ans进行二分(这种求最大值的最小,最小值的最大一般都是二分啦)
对于每一个节点$i$维护一堆$(a,b)$表示当前子树存在一条起点,终点到$i$距离分别为$a,b$且最长路径不超过$ans$的遍历完整棵子树的方案
现在讲如何合并
因为这是满二叉树,所以当前点i的方案肯定是从左儿子$lc$走到右儿子$rc$,或者反过来.
假设起点在左儿子,那么方案就是$(a,b)=(lc.a+cost_left,rc.b+cost_right)。
但是这个过程中还有$lc.end -> i -> rc.start$这条路径
所以还要满足$lc.b+cost_left+cost_right+rc.a<=ans$
但是,我们不可能对于每个$(lc.a,lc.b)$扫描所有的$(rc.a,rc.b)$
因此,对于相同的a,我们只保留最小的b,b也是同理。
另外对于$(a,b)$,如果能找到$(c,d)$使得$c<=a,d<=b$那么(a,b)就没有保留的必要
所以,我们在回溯时把每个节点的(a,b)对a从小到大排序
因为a递增,所以b递减(否则就没有保留的必要)
所以我们在合并时可以用双指针来加速合并(具体看代码)。
至于合并起点在lc内与起点在rc内的情况,可以用归并排序的思想
时间复杂度为$O(n*logn*logv)$
如果改为用数组来存每个节点的vector的话会快很多
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
#define N 140000
#define int long long
#define pr pair<int,int>
vector<pr> vec[N],_left,_right;
int mid;
vector<pr> dfs(int id,int from)
{
//cout<<id<<" "<<from<<endl;
vector<pr> lc,rc,now;
int cl=-1,cr;
if(vec[id].size()==1&&from)
{
now.push_back(make_pair(0,0));
return now;
}
for(int i=0;i<vec[id].size();i++)
{
pr l=vec[id][i];
if(l.first==from) continue;
vector<pr> t=dfs(l.first,id);//返回的(a,b)的a递增,因而b递减
if(cl<0) lc=t,cl=l.second;
else rc=t,cr=l.second;
}
int j=0;
for(int i=0;i<lc.size();i++)//选取起点在左子树,满足条件的、最优的(a,b),新的(a,b)=(lc[i].first,rc[i].second)
{
while(j+1<rc.size() && lc[i].second+rc[j+1].first+cl+cr<=mid) j++;//寻找rc中满足条件的b最小的(a,b)。因为a相同时只取最小的b
if(rc.size()&&lc[i].second+rc[j].first+cl+cr<=mid) _left.push_back(make_pair(lc[i].first+cl,rc[j].second+cr));//前面的(a,b)肯定会比当前的要差,因此不用查找rc[1~j-1]
}
j=0;
for(int i=0;i<rc.size();i++)//选取起点在右子树,满足条件的、最优的(a,b),新的(a,b)=(rc[i].first,lc[i].second)
{
while(j+1<lc.size() && rc[i].second+lc[j+1].first+cl+cr<=mid) j++;
if(lc.size()&&rc[i].second+lc[j].first+cl+cr<=mid) _right.push_back(make_pair(rc[i].first+cr,lc[j].second+cl));
}
//此时的_left和_right一定是有序的,现在要合成一个a递增,递减的数组
int l=0,r=0,last=0x7FFFFFFFFFFFFFFF;
while(l<_left.size()||r<_right.size())//类似归并排序的思想
{
if(l<_left.size()&&(r>=_right.size()||_left[l]<=_right[r]))
{
if(_left[l].second<last)//为了保证now里都是最优,b要递减
{
last=_left[l].second;
now.push_back(_left[l]);
}
l++;
}
else
{
if(_right[r].second<last)
{
last=_right[r].second;
now.push_back(_right[r]);
}
r++;
}
}
_left.clear(),_right.clear();
return now;
}
signed main()
{
int n;
cin>>n;
for(int i=2;i<=n;i++)
{
int a,v;
scanf("%lld%lld",&a,&v);
vec[a].push_back(make_pair(i,v));
vec[i].push_back(make_pair(a,v));
}
int l=0,r=17179869184;
while(r-l>1)
{
mid=(l+r)/2;
//cout<<l<<" "<<mid<<" "<<r<<endl;
if(!dfs(1,0).empty()) r=mid;
else l=mid;
}
mid=l;
if(!dfs(1,0).empty()) cout<<l;
else cout<<r;
}
[AGC007E] Shik and Travel的更多相关文章
- AGC007E Shik and Travel 解题报告
AGC007E Shik and Travel 题目大意:\(n\) 个点的二叉树,每个点要么两个儿子,要么没有儿子,每条边有边权. 你从 \(1\) 号节点出发,走到一个叶子节点.然后每一天,你可以 ...
- AtCoder AGC007E Shik and Travel (二分、DP、启发式合并)
题目链接 https://atcoder.jp/contests/agc007/tasks/agc007_e 题解 首先有个很朴素的想法是,二分答案\(mid\)后使用可行性DP, 设\(dp[u][ ...
- 【AtCoder Grand Contest 007E】Shik and Travel [Dfs][二分答案]
Shik and Travel Time Limit: 50 Sec Memory Limit: 512 MB Description 给定一棵n个点的树,保证一个点出度为2/0. 遍历一遍,要求每 ...
- [AT2172] [agc007_e] Shik and Travel
题目链接 AtCoder:https://agc007.contest.atcoder.jp/tasks/agc007_e 洛谷:https://www.luogu.org/problemnew/sh ...
- AtCoder Grand Contest 007 E:Shik and Travel
题目传送门:https://agc007.contest.atcoder.jp/tasks/agc007_e 题目翻译 现在有一个二叉树,除了叶子每个结点都有两个儿子.这个二叉树一共有\(m\)个叶子 ...
- AT2172 Shik and Travel
题目描述: luogu 题解: 二分+暴力$vector$+$dfs$. 记录下所有可能的子树内合法方案,双指针+归并合并. 代码: #include<vector> #include&l ...
- [atAGC007E]Shik and Travel
二分枚举答案,判定答案是否合法 贪心:每一个叶子只能经过一遍,因此叶子的顺序一定是一个dfs序,即走完一棵子树中的所有叶子才会到子树外 根据这个贪心可以dp,设$f[k][l][r]$表示仅考虑$k$ ...
- AtCoder刷题记录
构造题都是神仙题 /kk ARC066C Addition and Subtraction Hard 首先要发现两个性质: 加号右边不会有括号:显然,有括号也可以被删去,答案不变. \(op_i\)和 ...
- 贪心/构造/DP 杂题选做Ⅲ
颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...
随机推荐
- git在使用push指令的时候产生的错误
一.问题我们在使用git指令的时候往往会出现如下错误. $ git push -u origin master To https://github.com/pzq7025/ss-fly.git ! [ ...
- Java学习:JDBC各类详解
JDBC各个类详解 代码实现: //1.导入驱动jar包 //2.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //3.获取数据库连对象 ...
- golang ---Learn Concurrency
https://github.com/golang/go/wiki/LearnConcurrency 实例1: package main import ( "fmt" " ...
- Windows下非PE方式载荷投递方式研究
0. 引言 0x1:载荷是什么?在整个入侵过程中起到什么作用? 载荷的作用在整个入侵链路的作用起到纽带的作用,它借助于目标系统提供的某些功能:组件:执行环境,将攻击者的传递的恶意payload包裹起来 ...
- Oracle学习笔记(五)
如何查询硬解析问题: --捕获出需要使用绑定变量的SQL drop table t_bind_sql purge; create table t_bind_sql as select sql_text ...
- 华为 S5700 交换机 批量修改端口方法
常常在配置交换机端口的时候需要将多个端口设置为相同的配置,当时各端口逐一去配置不仅慢,而且容易出错,这个时候就需要对端口进行批量设置,不仅快捷,而且避免了反复输出容易出错的情况.不同系列.不同版本交换 ...
- C# vb .net图像合成-合成椭圆
在.net中,如何简单快捷地实现图像合成呢,比如合成文字,合成艺术字,多张图片叠加合成等等?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码 ...
- C++:构造函数
问题提出 默认初始化 答案 ▶问题提出 主要是在VC++ 2015里经常提示莫名其妙的编译错误. 分析一下,为什么Java里构造函数这个问题很简单: 1. C++里对象类型不止有按引用传递,还可能拷贝 ...
- Mybatis源码解析(二) —— 加载 Configuration
Mybatis源码解析(二) -- 加载 Configuration 正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...
- Cheat Engine 字节数组类型
BIG5 编码:http://www.qqxiuzi.cn/zh/hanzi-big5-bianma.php 打开游戏 准备修改名字 查找BIG5码 藤 吉 开始扫描 使用字节数组类型扫描 新BIG5 ...