[AT2172] [agc007_e] Shik and Travel
题目链接
AtCoder:https://agc007.contest.atcoder.jp/tasks/agc007_e
洛谷:https://www.luogu.org/problemnew/show/AT2172
Solution
首先由于每条边只能经过两次,所以每次到了\(x\)点就会遍历完\(x\)子树再出来。
考虑二分答案,设当前二分的答案为\(mid\)。
设二元组\((a,b)_x\)表示\(x\)子树下第一次走代价为\(a\)最后一次为\(b\),且中间的过程都\(\leqslant \rm mid\)的一种方案。
那么显然可以得到一个暴力:
我们暴力枚举当前点左儿子和右儿子的二元组,设为\((a,b)_{ls},(c,d)_{rs}\),设左儿子的边权为\(x\),右儿子为\(y\),那么如果满足:\(b+c+x+y\leqslant {\rm mid}\),那么我们可以得到一个新的二元组\((a+x,d+y)_x\)。
考虑如何优化这个玩意,显然对于一个\(a\)只需要一个最小的\(b\),对于\(b\)也同理。
所以显然可以使用\(\rm two \ pointer\)做到每次合并为\(O(sz)\),其中\(sz\)为个数之和。
但是这样可以被卡成\(O(n^2)\),我们加一个启发式合并,每次合并小的,那么每次最多就是\(2|\min(S_{ls},S_{rs})|\)的,总复杂度\(O(n\log^2 n\log \rm ans)\),如果归并排序的话每次处理都是有序的,复杂度可以降为\(O(n\log n \log \rm ans)\)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double
#define ll long long
#define pii pair<int,int >
#define fr first
#define sc second
#define mp make_pair
#define vec vector<pii >
#define pb push_back
#define iter vector <pii > :: iterator
#define for_vec(i,x) for(iter i=x.begin();i!=x.end();i++)
#define _sort(x,y) sort(x.begin(),x.end(),y)
#define I (int)
const int maxn = 3e5+10;
const int inf = 1e9;
const lf eps = 1e-8;
int cmpx(pii x,pii y) {return x.fr<y.fr;}
int cmpy(pii x,pii y) {return x.sc<y.sc;}
int son[maxn][2],v[maxn][2],n,d[maxn],mid,mn[maxn];
vec f[maxn];
void solve(vec &ls,vec &rs,vec &s) {
if(ls.size()>rs.size()) swap(ls,rs); //启发式合并
if(!s.empty()) s.clear();
_sort(ls,cmpy),_sort(rs,cmpx);
mn[0]=rs[0].sc;for(int i=1;i<I rs.size();i++) mn[i]=min(mn[i-1],rs[i].sc);
int p1=0,p2=rs.size()-1;
for(;p1<I ls.size();p1++) {
while(p2>=0&&ls[p1].sc+rs[p2].fr>mid) p2--;
if(~p2) s.pb(mp(ls[p1].fr,mn[p2]));
}
_sort(ls,cmpx),_sort(rs,cmpy);
mn[0]=rs[0].fr;for(int i=1;i<I rs.size();i++) mn[i]=min(mn[i-1],rs[i].fr);
for(p1=0,p2=rs.size()-1;p1<I ls.size();p1++) {
while(p2>=0&&ls[p1].fr+rs[p2].sc>mid) p2--;
if(~p2) s.pb(mp(mn[p2],ls[p1].sc));
}
}
void dfs(int x) {
if(!son[x][0]) return f[x].pb(mp(0,0)),void();
int l=son[x][0],r=son[x][1];dfs(l),dfs(r);
vec &ls=f[l],&rs=f[r];
for_vec(i,ls) i->fr+=v[x][0],i->sc+=v[x][0];
for_vec(i,rs) i->fr+=v[x][1],i->sc+=v[x][1];
solve(ls,rs,f[x]);ls.clear(),rs.clear();
}
signed main() {
read(n);
for(int i=2,x,y;i<=n;i++) read(x),read(y),son[x][d[x]]=i,v[x][d[x]++]=y;
int l=0,r=1e10,ans=1e10;
while(l<=r) {
mid=(l+r)>>1,dfs(1);
if(f[1].empty()) l=mid+1;
else r=mid-1,ans=mid;f[1].clear();
}write(ans);
return 0;
}
[AT2172] [agc007_e] Shik and Travel的更多相关文章
- 【AtCoder Grand Contest 007E】Shik and Travel [Dfs][二分答案]
Shik and Travel Time Limit: 50 Sec Memory Limit: 512 MB Description 给定一棵n个点的树,保证一个点出度为2/0. 遍历一遍,要求每 ...
- AGC007E Shik and Travel 解题报告
AGC007E Shik and Travel 题目大意:\(n\) 个点的二叉树,每个点要么两个儿子,要么没有儿子,每条边有边权. 你从 \(1\) 号节点出发,走到一个叶子节点.然后每一天,你可以 ...
- AT2172 Shik and Travel
题目描述: luogu 题解: 二分+暴力$vector$+$dfs$. 记录下所有可能的子树内合法方案,双指针+归并合并. 代码: #include<vector> #include&l ...
- AtCoder Grand Contest 007 E:Shik and Travel
题目传送门:https://agc007.contest.atcoder.jp/tasks/agc007_e 题目翻译 现在有一个二叉树,除了叶子每个结点都有两个儿子.这个二叉树一共有\(m\)个叶子 ...
- AtCoder AGC007E Shik and Travel (二分、DP、启发式合并)
题目链接 https://atcoder.jp/contests/agc007/tasks/agc007_e 题解 首先有个很朴素的想法是,二分答案\(mid\)后使用可行性DP, 设\(dp[u][ ...
- [AGC007E] Shik and Travel
题目 给定一棵n节点的 以1为根的 满二叉树 (每个非叶子节点恰好有两个儿子)n−1 条边. 第ii条边连接 i+1号点 和 ai, 经过代价为vi设这棵树有m个叶子节点定义一次合法的旅行为:(1) ...
- [atAGC007E]Shik and Travel
二分枚举答案,判定答案是否合法 贪心:每一个叶子只能经过一遍,因此叶子的顺序一定是一个dfs序,即走完一棵子树中的所有叶子才会到子树外 根据这个贪心可以dp,设$f[k][l][r]$表示仅考虑$k$ ...
- 贪心/构造/DP 杂题选做Ⅲ
颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...
- AtCoder Grand Contest 007
AtCoder Grand Contest 007 A - Shik and Stone 翻译 见洛谷 题解 傻逼玩意 #include<cstdio> int n,m,tot;char ...
随机推荐
- KVM虚拟机无法启动
一.启动虚拟机报错: [root@KVM ~]# virsh start node-mssql-test01 error: Failed to start domain node-mssql-test ...
- 收集的PHP工具及类库
composer PHP的依赖管理工具 phpmig PHP的数据库迁移工具,依赖于composer Requests for PHP HTTP请求库,采集页面可以用到的 ...
- HCIE理论-IPV6
ipv4与ipv6的对比 IPv4 :32 bit 点分十进制 192.168.1.1 2^32=42.9亿 ipv4地址不足IPv6 :128 bit 十六进制 2^128 冒号分十六进制ipv4 ...
- 函数parseQuery用于解析url查询参数
在百度上找的,以后忘了再看. 语法如下: var obj = parseQuery(query) query是被解析的查询参数,函数返回解析后的对象. 使用范例如下: var jerry = pars ...
- AtCoder Regular Contest 101 D - Median of Medians
二分答案 然后前缀和+树状数组来判断这个答案是否大于等于数 如果我们对于一个查询,如果小于这个数令为1,大于这个数领为-1 将所有前缀和放在树状数组中,就可以查询所有sum_{l} < sum_ ...
- 优化 VR 动作类游戏《Space Pirate Trainer*》以便在英特尔® 集成显卡上实现卓越的表现
Space Pirate Trainer* 是一款面向 HTC Vive*.Oculus Touch* 和 Windows Mixed Reality* 的原创发行游戏.版本 1.0 于 2017 年 ...
- 小白初识 - 归并排序(MergeSort)
归并排序是一种典型的用分治的思想解决问题的排序方式. 它的原理就是:将一个数组从中间分成两半,对分开的两半再分成两半,直到最终分到最小的单位(即单个元素)的时候, 将已经分开的数据两两合并,并且在合并 ...
- 《零基础学JavaScript(全彩版)》学习笔记
<零基础学JavaScript(全彩版)>学习笔记 二〇一九年二月九日星期六0时9分 前期: 刚刚学完<零基础学HTML5+CSS3(全彩版)>,准备开始学习JavaScrip ...
- Python常用模块之VideoCapture
官方网址:http://videocapture.sourceforge.net/ 功能介绍: VideoCapture是Win32版Python的一个扩展,可以访问视频采集设备(如USB摄像头) ...
- 月薪45K的Python爬虫工程师告诉你爬虫应该怎么学,太详细了!
想用Python做爬虫,而你却还不会Python的话,那么这些入门基础知识必不可少.很多小伙伴,特别是在学校的学生,接触到爬虫之后就感觉这个好厉害的样子,我要学.但是却完全不知道从何开始,很迷茫,学的 ...