Noip2018Day1T3 赛道修建
problem
给出一棵有边权的树。一条链的权值定义为该链所经过的边的边权值和。需要选出\(m\)条链,求\(m\)条链中权值最小的链的权值最大是多少。
solution
首先显然二分。
然后考虑如何判断二分出来的一个答案\(x\)是否是可行的。也就是能否选出\(m\)条链,每条链权值都大于等于\(x\)。这个其实是贪心。
定义直链为从一个某个点的祖先到该点的路径。
可以发现每条链要么就是一条直链,要么由两条直链在某个点处合并起来得到。
贪心的地方在于,对于每个点肯定都是优先将能合成的直链合成。然后再保证向上传递的直链长度最大。因为即便向上传递的长度特别大,产生的贡献也做多只能是\(1\)。所以要先保证在当前子树上合成最多的链。
然后问题就变成了在一棵子树内得到一些直链长度。现在把这些直链两两合并成权值大于等于\(x\)的链。然后保证剩下的直链长度最大。
这里可以二分答案一下。也可以用个\(multiset\)处理。反正是很可做的一个问题。
代码中有各档部分分,BF5为正解
code
#include<set>
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 100010;
ll read() {
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
int n,m;
struct node {
int v,nxt,w;
}e[N << 1];
int head[N],ejs;
void add(int u,int v,int w) {
e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;e[ejs].w = w;
}
namespace BF1 {
int dis[N];
void dfs(int u,int fa) {
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
dis[v] = dis[u] + e[i].w;
dfs(v,u);
}
}
void main() {
dfs(1,0);
int x = 0;
for(int i = 1;i <= n;++i) if(dis[i] > dis[x]) x = i;
// cout<<x<<endl;
memset(dis,0,sizeof(dis));
dfs(x,0);
int ans = 0;
for(int i = 1;i <= n;++i) ans = max(ans,dis[i]);
cout<<ans;
}
}
namespace BF2 {
int a[N],cnt;
int check(int x) {
int p = 1,ret = 0;
for(int i = cnt;i > p;--i) {
if(a[i] > x && i > p) {ret++;continue;}
while(a[p] + a[i] < x && p < i) ++p;
if(p < i) ret++,p++;
else break;
}
return ret;
}
void main() {
int l = 100000,r = 0;
for(int i = 1;i <= ejs;i += 2) a[++cnt] = e[i].w,l = min(l,a[cnt]),r += a[cnt];
sort(a + 1,a + cnt + 1);
int ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid) >= m) ans = mid,l = mid + 1;
else r = mid - 1;
}
cout<<ans<<endl;
}
}
int du[N];
namespace BF3 {
int a[N],cnt;
void dfs(int u,int fa) {
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
a[++cnt] = e[i].w;
dfs(v,u);
}
}
int check(int x) {
int now = 0,ret = 0;
for(int i = 1;i <= cnt;++i) {
now += a[i];
if(now >= x) now = 0,ret ++;
}
return ret;
}
void main() {
for(int i = 1;i <= n;++i)
if(du[i] == 1) {dfs(i,0);break;}
int l = 1000000,r = 0;
for(int i = 1;i <= ejs;i += 2) {
l = min(l,e[i].w);r += e[i].w;
}
int ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid) >= m) ans = mid,l = mid + 1;
else r = mid - 1;
}
cout<<ans<<endl;
}
}
int L = 100000,R;
namespace BF5 {
int ANS;
int dfs(int u,int fa,int x) {
multiset<int>s;
int ret = 0;
// if(!s.empty()) printf("%d\n",u);
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
int k = dfs(v,u,x);
if(k + e[i].w >= x) ANS++;
else s.insert(k + e[i].w);
}
while(!s.empty()) {
multiset<int>::iterator it = s.begin();
int k = *it;
s.erase(it);
multiset<int>::iterator is = s.lower_bound(x - k);
if(is == s.end()) ret = max(ret,k);
else ANS++,s.erase(is);
}
// s.clear();
// printf("%d %d\n",u,ret);
return ret;
}
void main() {
int l = L,r = R,ans = 0;
while(l <= r) {
int mid =(l + r) >> 1;
ANS = 0;dfs(1,0,mid);
if(ANS >= m) ans = mid,l = mid + 1;
else r = mid - 1;
}
cout<<ans;
}
}
int main() {
n = read(),m = read();
int bz1 = 1,bz2 = 1;
for(int i = 1;i < n;++i) {
int u = read(),v = read(),w = read();
L = min(L,w);R += w;
du[u]++;du[v]++;
add(u,v,w);add(v,u,w);
if(u != 1) bz1 = 0;
if(v != u + 1) bz2 = 0;
}
if(m == 1) {BF1::main();return 0;}
if(bz1) {BF2::main();return 0;}
if(bz2) {BF3::main();return 0;}
BF5::main();
return 0;
}
/*
7 1
1 2 10
1 3 5
2 4 9
2 5 8
3 6 6
3 7 7
*/
Noip2018Day1T3 赛道修建的更多相关文章
- Luogu5021 [NOIP2018]赛道修建
Luogu5021 [NOIP2018]赛道修建 一棵大小为 \(n\) 的树,边带权.选 \(m\) 条链使得长度和最小的链最大. \(m<n\leq5\times10^4\) 贪心,二分答案 ...
- [NOIp2018提高组]赛道修建
[NOIp2018提高组]赛道修建 题目大意: 给你一棵\(n(n\le5\times10^4)\)个结点的树,从中找出\(m\)个没有公共边的路径,使得第\(m\)长的路径最长.问第\(m\)长的路 ...
- noip2018 D1T3 赛道修建
题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 mm 条赛道. C 城一共有 nn 个路口,这些路口编号为 1,2,…,n1,2,…,n,有 n-1n−1 条适合于修建赛道的双向通 ...
- noip 2018 D1T3 赛道修建
noip 2018 D1T3 赛道修建 首先考虑二分答案,这时需要的就是对于一个长度求出能在树中选出来的最多的路径条数.考虑到一条路径是由一条向上的路径与一条向下的路径构成,或者仅仅是向上或向下的路径 ...
- 【LG5021】[NOIP2018]赛道修建
[LG5021][NOIP2018]赛道修建 题面 洛谷 题解 NOIP之前做过增强版还没做出来\(QAQ\) 一看到题目中的最大值最小,就很容易想到二分答案 重点是考虑如何\(check\) 设\( ...
- 【noip2018】【luogu5021】赛道修建
题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 mm 条赛道. C 城一共有 nn 个路口,这些路口编号为 1,2,…,n1,2,…,n,有 n-1n−1 条适合于修建赛道的双向通 ...
- 竞赛题解 - NOIP2018 赛道修建
\(\mathcal {NOIP2018}\) 赛道修建 - 竞赛题解 额--考试的时候大概猜到正解,但是时间不够了,不敢写,就写了骗分QwQ 现在把坑填好了~ 题目 (Copy from 洛谷) 题 ...
- [NOIP2018TG]赛道修建
[NOIP2018TG]赛道修建 考场上multiset调不出啊啊啊!!! 首先肯定是二分答案 做树形dp,f[i]表示i点的子树两两匹配后剩下的最长长度 匹配可以用multiset维护 但是菊花图跑 ...
- 【题解】 P5021赛道修建
[题解]P5021 赛道修建 二分加贪心,轻松拿省一(我没有QAQ) 题干有提示: 输出格式: 输出共一行,包含一个整数,表示长度最小的赛道长度的最大值. 注意到没,最小的最大值,还要多明显? 那么我 ...
随机推荐
- Refresh design pattern
多年后, 再次翻阅设计模式书籍, 将每种模式的要点总结于此, 需要本身有一定设计模式基础, 再结合要点, 帮助更好理解与运用. 策略模式 (Strategy) 附加功能单独抽象不同接口 功能以组合方式 ...
- VScode - 10个提高工作效率的快捷键
复制行 shift + alt + up / down 移动行 alt + up / down 多选 按住alt + 鼠标单击 删除上个单词 ctrl + Backspace 全部保存 ctrl + ...
- Android五大布局详解——GridLayout(网格布局)
GridLayout 本章以一个小的实现示例讲述: 实现效果如图: 代码实现: <?xml version="1.0" encoding="utf-8"? ...
- 工具-Xmind常用快捷键/使用
1-快捷键 Ctrl+Shift+L 快捷键助手 Ctrl+Home 返回中心主题 Ctrl+] 插入摘要 Ctrl+I 插入图片 Ctrl+Shift+H 插入超链接 Ctrl+1,2,3,4,5, ...
- MySQL优化/面试,看这一篇就够了
原文链接:http://www.zhenganwen.top/articles/2018/12/25/1565048860202.html 作者:Anwen~链接:https://www.nowcod ...
- 强大的django-debug-toolbar,django项目性能分析工具
强大的django-debug-toolbar,django项目性能分析工具 给大家介绍一个用于django中debug模式下查看网站性能等其他信息的插件django-debug-toolbar 首先 ...
- C#的语法----程序结构(1)
接下来的内容是整个C#学习的脉络,它将各个知识点串联了起来,是整个C#的重点,所以篇幅较长. 首先,我们类比一下PLC和C#执行代码的方式,其实不难发现都是顺序扫描,以Main为程序入口,从上到下一行 ...
- 【朝花夕拾】Android自定义View篇之(二)Canvas常用功能
前言 转在请申明,转自[https://www.cnblogs.com/andy-songwei/p/10960012.html],谢谢! 上一篇讲View的绘制流程中讲到过,最后一步是draw流程, ...
- Python 从入门到进阶之路(七)
之前的文章我们简单介绍了一下 Python 中异常处理,本篇文章我们来看一下 Python 中 is 和 == 的区别及深拷贝和浅拷贝. 我们先来看一下在 Python 中的双等号 == . == 是 ...
- 什么是EAC模型
在20世纪70年代末,一个心理学学生理查德•班德勒和一个语言学学生约翰•格林德提出了一个EAC模型,即眼睛解读线索.这个模型对不同的感官和思维方式之间进行一些有效的研究, 对于大部分的人来说,左边往往 ...