题目描述

\(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\)它呢?首先,我们先分析路径的状态。

一条路径在树上有两种状态:

  1. 挂在一个节点下面的一条链。
  2. 两条挂在节点下的链组成的路径。

很显然,我们需要一种方法使得所有路径长度大于等于当前\(x\)的数量最大。

我们可以发现,若以一个节点为根节点的子树有残留的形如\(1\)的链有两种选择。

  1. 将其和其他的链进行配对形成如同\(2\)所说的链对。
  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-赛道修建的更多相关文章

  1. Luogu5021 [NOIP2018]赛道修建

    Luogu5021 [NOIP2018]赛道修建 一棵大小为 \(n\) 的树,边带权.选 \(m\) 条链使得长度和最小的链最大. \(m<n\leq5\times10^4\) 贪心,二分答案 ...

  2. [NOIp2018提高组]赛道修建

    [NOIp2018提高组]赛道修建 题目大意: 给你一棵\(n(n\le5\times10^4)\)个结点的树,从中找出\(m\)个没有公共边的路径,使得第\(m\)长的路径最长.问第\(m\)长的路 ...

  3. noip2018 D1T3 赛道修建

    题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 mm 条赛道. C 城一共有 nn 个路口,这些路口编号为 1,2,…,n1,2,…,n,有 n-1n−1 条适合于修建赛道的双向通 ...

  4. noip 2018 D1T3 赛道修建

    noip 2018 D1T3 赛道修建 首先考虑二分答案,这时需要的就是对于一个长度求出能在树中选出来的最多的路径条数.考虑到一条路径是由一条向上的路径与一条向下的路径构成,或者仅仅是向上或向下的路径 ...

  5. 【LG5021】[NOIP2018]赛道修建

    [LG5021][NOIP2018]赛道修建 题面 洛谷 题解 NOIP之前做过增强版还没做出来\(QAQ\) 一看到题目中的最大值最小,就很容易想到二分答案 重点是考虑如何\(check\) 设\( ...

  6. 【noip2018】【luogu5021】赛道修建

    题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 mm 条赛道. C 城一共有 nn 个路口,这些路口编号为 1,2,…,n1,2,…,n,有 n-1n−1 条适合于修建赛道的双向通 ...

  7. 竞赛题解 - NOIP2018 赛道修建

    \(\mathcal {NOIP2018}\) 赛道修建 - 竞赛题解 额--考试的时候大概猜到正解,但是时间不够了,不敢写,就写了骗分QwQ 现在把坑填好了~ 题目 (Copy from 洛谷) 题 ...

  8. [NOIP2018TG]赛道修建

    [NOIP2018TG]赛道修建 考场上multiset调不出啊啊啊!!! 首先肯定是二分答案 做树形dp,f[i]表示i点的子树两两匹配后剩下的最长长度 匹配可以用multiset维护 但是菊花图跑 ...

  9. 【题解】 P5021赛道修建

    [题解]P5021 赛道修建 二分加贪心,轻松拿省一(我没有QAQ) 题干有提示: 输出格式: 输出共一行,包含一个整数,表示长度最小的赛道长度的最大值. 注意到没,最小的最大值,还要多明显? 那么我 ...

  10. P5021 赛道修建[贪心+二分]

    题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 mm 条赛道. C 城一共有 nn 个路口,这些路口编号为 1,2,-,n1,2,-,n,有 n-1n−1 条适合于修建赛道的双向通 ...

随机推荐

  1. socket 异步 发送 接收 数据

    Socket socketClints = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); / ...

  2. javascript中的原型和原型链(一)

    原型和原型链是 JS 中不可避免需要碰到的知识点,本文使用图片思维导图的形式缕一缕原型.原型链.实例.构造函数等等概念之间的关系. Constructor 构造函数 首先我们先写一个构造函数 Pers ...

  3. hdu_3535 (AreYouBusy)

    http://acm.hdu.edu.cn/showproblem.php?pid=3535 题意:        给你n个工作集合,给你T的时间去做它们.给你m和s,说明这个工作集合有m件事可以做, ...

  4. Python3学习笔记(十一):函数参数详解

    一.位置参数 根据参数的位置来传递参数,调用函数时,传递的参数顺序和个数必须和定义时完全一致 # 定义函数 def man(name, age): print("My name is %s, ...

  5. 错误“Object reference not set to an instance of an object”的解决方法

    在进行unity游戏制作的C#代码编写时,会遇到“NullReferenceException: Object reference not set to an instance of an objec ...

  6. redis high available solution/ redis 高可用方案

    http://developers.linecorp.com/blog/?p=1420 http://engineering.docusign.com/articles/redis-sentinel- ...

  7. 微信、QQ第三方登录授权时的问题总结

    一.微信第一个问题:redirect_uri域名与后台配置不一致,错误码:10003 解决方案: 1,首先确定访问的第三方接口地址参数前后顺序是否正确,redirect_uri回调地址是否加了http ...

  8. 利用docker启动 wordpress

    网上有很多教程哈,我只是记录自己怎么玩的,没啥教学意义 查看镜像说明的mysql/data目录,方便挂载 [root@docker ~]# docker inspect -f {{.Config.Vo ...

  9. C++抽象类实践

    实践如下: #include <iostream> using namespace std; class Service { public: // 有一个虚函数即为抽象类 int id; ...

  10. day7_集合,深浅copy

    一.集合 集合是无序的,不重复的数据集合,其元素为可哈希(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键).以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. 关系测试, ...