noip2018day1-赛道修建
题目描述
\(C\) 城将要举办一系列的赛车比赛。在比赛前,需要在城内修建 $m $条赛道。
\(C\) 城一共有 \(n\) 个路口,这些路口编号为 \(1,2,…,n\)有 $n-1 $条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第 $i $条道路连接的两个路口编号为 \(a_i\) 和 \(b_i\),该道路的长度为 \(l_i\)。借助这 \(n-1\) 条道路,从任何一个路口出发都能到达其他所有的路口。
一条赛道是一组互不相同的道路 \(e_1,e_2,…,e_k\),满足可以从某个路口出发,依次经过 道路 \(e_1,e_2,…,e_k\)(每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。
目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的 \(m\)条赛道中长度最小的赛道长度最大(即 \(m\) 条赛道中最短赛道的长度尽可能大)
Input
输入文件第一行包含两个由空格分隔的正整数 \(n,m\),分别表示路口数及需要修建的 赛道数。
接下来 \(n-1\) 行,第 \(i\)行包含三个正整数 \(a_i,b_i,l_i\),表示第 \(i\) 条适合于修建赛道的道 路连接的两个路口编号及道路长度。
保证任意两个路口均可通过这 \(n-1\) 条道路相互到达。
每行中相邻两数之间均由一个空格分隔。
对于所有的数据, \(2 ≤ n ≤ 50,000\), \(1 ≤ m ≤ n-1\), \(1 ≤ a_i,b_i ≤ n\), \(1 ≤ l_i ≤ 10,000\)。
Output
输出共一行,包含一个整数,表示长度最小的赛道长度的最大值。
Sample Input
7 1
1 2 10
1 3 5
2 4 9
2 5 8
3 6 6
3 7 7
9 3
1 2 6
2 3 3
3 4 5
4 5 10
6 2 4
7 2 9
8 4 7
9 4 4
Sample Output
31
15
很自然的想到是二分。。。
话说为什么联赛有那么多二分???
最小值最大,标准的二分询问。
我们可以去二分它的最小值最大的答案,然后去\(check\)它。
怎么去\(check\)它呢?首先,我们先分析路径的状态。
一条路径在树上有两种状态:
- 挂在一个节点下面的一条链。
- 两条挂在节点下的链组成的路径。
很显然,我们需要一种方法使得所有路径长度大于等于当前\(x\)的数量最大。
我们可以发现,若以一个节点为根节点的子树有残留的形如\(1\)的链有两种选择。
- 将其和其他的链进行配对形成如同\(2\)所说的链对。
- 不满足条件\(1\),将其作为子链的一部分并到其父亲节点上。
很显然,只有这样才可以保证所有的链数量最多。
那么,我们该如何维护这个过程呢?
利用\(mutilset\)动态的维护这个过程就可以了。
当然,作为一名\(oier\)只写一个正解是不太友善的。。。万一挂了呢?
我们要切分!!!
特殊情况一:链
对于一条链,问题变成了有\(n\)个数,将其分为\(m\)个不重叠的区间并且每个区间和最大。
这个问题可以直接二分求解,\(check\)函数如下:
bool check(int x){
int Sum=0,tot=0;
rep(i,1,n){
Sum+=A[i];
if(Sum>=x)Sum=0,tot++;
}
return tot>=m;
}
特殊情况二:菊花图
对于菊花图也还是直接二分。
很显然,我们可以先将所有的边按照边权进行一波排序。
然后,我们开两个指针分别指向开头和结尾。
我们再移动指针直到其相遇。
我们可以发现,一条长度已经大于等于当前\(x\)的边可以直接当一条路径。
而未到当前长度的边可以从尾指针向前找到一个相加可以满足条件的边即可。
bool check(int x){
int posl=0,posr=cnt,tot=0;
while(posr>posl) {
if(A[posr]>=x)posr--,tot++;
else {
while(posl<posr&&A[posl]+A[posr]<x)posl++;
if(posl==posr)continue;
posr--,posl++,tot++;
}
}
return tot>=m;
}
代码如下
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
inline int Read() {
int res = 0, f = 1;
char c;
while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
do res = (res << 3) + (res << 1) + (c ^ 48);
while (c = getchar(), c >= 48 && c <= 57);
return f ? res : -res;
}
template<class T>inline bool Min(T &a, T const&b) {
return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
return a < b ? a = b, 1 : 0;
}
const int N=5e4+5,M=5e4+5,mod=1e9+7;
bool MOP1;
int n,m,L,R;
struct Link_list {
int Tot,Head[N],to[M<<1],Nxt[M<<1],cost[M<<1];
inline void clear(void) {
Tot=0;
memset(Head,0,sizeof Head);
}
inline void AddEdgepair(const int a,const int b,const int c) {
to[++Tot]=b,cost[Tot]=c,Nxt[Tot]=Head[a],Head[a]=Tot;
to[++Tot]=a,cost[Tot]=c,Nxt[Tot]=Head[b],Head[b]=Tot;
}
} G;
struct T3flower {
int Ans,cnt,A[N];
inline bool check(const int x) {
int posl=0,posr=cnt,tot=0;
while(posr>posl) {
if(A[posr]>=x)posr--,tot++;
else {
while(posl<posr&&A[posl]+A[posr]<x)posl++;
if(posl==posr)continue;
posr--,posl++,tot++;
}
}
return tot>=m;
}
inline void solve(void) {
int Ans=0;
cnt=0;
erep(i,G,1) {
int y=G.to[i];
A[++cnt]=G.cost[i];
}
sort(A+1,A+cnt+1);
while(L<=R) {
int mid=(L+R)>>1;
if(check(mid))Ans=mid,L=mid+1;
else R=mid-1;
}
printf("%d\n",Ans);
}
} Pflower;
int deg[N];
struct T3list {
int cnt,A[N];
inline bool check(const int x) {
int Sum=0,tot=0;
rep(i,1,n-1) {
Sum+=A[i];
if(Sum>=x)Sum=0,tot++;
}
return tot>=m;
}
void dfs(const int x,const int pre) {
erep(i,G,x) {
int y=G.to[i];
if(y==pre)continue;
A[++cnt]=G.cost[i];
dfs(y,x);
}
}
inline void solve(void) {
int Ha=0;
cnt=0;
rep(i,1,n)if(deg[i]==1) {
Ha=i;
break;
}
dfs(Ha,0);
int Ans=0;
while(L<=R) {
int mid=(L+R)>>1;
if(check(mid))Ans=mid,L=mid+1;
else R=mid-1;
}
printf("%d\n",Ans);
}
} Plist;
struct T330 {
int tot;
void dfs(const int x,const int pre,int &len,const int mid) {
multiset<int>v;
erep(i,G,x) {
int y=G.to[i];
if(y==pre)continue;
int res=0;
dfs(y,x,res,mid),res+=G.cost[i];
if(res>=mid)tot++;
else v.insert(res);
}
len=0;
while(!v.empty()) {
int res=*v.begin();
v.erase(v.begin());
set<int>::iterator it=v.lower_bound(mid-res);
if(it!=v.end())v.erase(it),tot++;
else Max(len,res);
}
}
inline void solve(void) {
int Ans=0;
while(L<=R) {
int mid=(L+R)>>1,ans=0;
tot=0,dfs(1,0,ans,mid);
if(tot>=m)Ans=mid,L=mid+1;
else R=mid-1;
}
printf("%d\n",Ans);
}
} P100;
int Ma,w;
void find(const int x,const int pre,const int s) {
if(s>Ma)Ma=s,w=x;
erep(i,G,x) {
int y=G.to[i];
if(y==pre)continue;
find(y,x,s+G.cost[i]);
}
}
bool MOP2;
inline void _main() {
n=Read(),m=Read();
L=1e9;
int flag_flower=1,flag_list=1;
ret(i,1,n) {
int a=Read(),b=Read(),c=Read();
Min(L,c),R+=c;
if(a!=1)flag_flower=0;
if(b!=a+1)flag_list=0;
deg[a]++,deg[b]++;
G.AddEdgepair(a,b,c);
}
R/=m;
find(1,-1,0),find(w,-1,0),Min(R,Ma);
if(m==1)printf("%d\n",Ma);
else if(flag_flower)Pflower.solve();
else if(flag_list)Plist.solve();
else P100.solve();
}
signed main() {
_main();
return 0;
}
noip2018day1-赛道修建的更多相关文章
- 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) 题干有提示: 输出格式: 输出共一行,包含一个整数,表示长度最小的赛道长度的最大值. 注意到没,最小的最大值,还要多明显? 那么我 ...
- P5021 赛道修建[贪心+二分]
题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 mm 条赛道. C 城一共有 nn 个路口,这些路口编号为 1,2,-,n1,2,-,n,有 n-1n−1 条适合于修建赛道的双向通 ...
随机推荐
- LeetCode 141、142环形链表
141题: 首先,先看141题,这个题是比较初级也是比较经典的环形链表题: 给定一个链表,判断链表中是否有环. 进阶:你能否不使用额外空间解决此题? 那么,什么是有环的链表呢: 这个就是有环的链表 题 ...
- js获取页面的各种高度与宽度
document.body.scrollTop等属性可以获取页面滚动距离等,但是此类属性在xhtml标准网页或者更简单的说是带<!DOCTYPE ..>标签的页面里得到的结果是0, 所以一 ...
- python学习之路(19)
匿名函数 当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便. 在Python中,对匿名函数提供了有限支持.还是以map()函数为例,计算f(x)=x2时,除了定义一个f(x) ...
- Linux上Redis安装和简单操作
Redis redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorte ...
- localhost与127.0.0.1的区别是什么?
localhost与127.0.0.1的区别是什么?都代表本地服务器 相信有人会说是本地ip,曾有人说,用127.0.0.1比localhost好,可以减少一次解析. 看来这个问题还有人不清楚,其实这 ...
- MQTT消息中间件Mosquitto的安装和配置
特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...
- 2019秋Java学期课程总结
眨眼间,Java这门课程就快要到了尾声,这门课程主要学习到 搭建了Java的编译环境,安装eclipse软件,会用Java写一些简单的程序. 主要学习到的知识点有以下几点 1:通过写pta上的作业知道 ...
- nginx location正则
nginx location正则写法 一个示例: location = / { # 精确匹配 / ,主机名后面不能带任何字符串 [ configuration A ] } location / { # ...
- UTC日期转时间戳
网上的方法用mktime来转换日期到时间戳,会被当前环境的时区影响,现在这么做,用UTC的日期转时间戳这样要转换各地的时区也简单 unsigned long utcMktime(const unsig ...
- Learn The Architecture Memory Management 译文
1.概述 本文档介绍了ARMv8-A架构内存管理的关键——内存地址转换,包括虚拟地址(VA)到物理地址(PA)的转换.页表(或称地址转换表)格式以及TLBs(Translation Lookaside ...