题目描述

C 城将要举办一系列的赛车比赛。在比赛前,需要在城内修建 mm 条赛道。

C 城一共有 nn 个路口,这些路口编号为 1,2,…,n1,2,…,n,有 n-1n−1 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第 ii 条道路连接的两个路口编号为 a_ia**i 和 b_ib**i,该道路的长度为 l_il**i。借助这 n-1n−1 条道路,从任何一个路口出发都能到达其他所有的路口。

一条赛道是一组互不相同的道路 e_1,e_2,…,e_ke1,e2,…,e**k,满足可以从某个路口出发,依次经过 道路 e_1,e_2,…,e_ke1,e2,…,e**k(每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。

目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的 mm 条赛道中长度最小的赛道长度最大(即 mm 条赛道中最短赛道的长度尽可能大)

解析

NOIp的题真的是越来越难啊。。。

首先容易看出是个二分。但是看出来还不够,这题检验就比较恶心,我没想出来(太菜了)。

一开始的想法是计算出一个根下所有点对的路径长,然后贪心地每次把满足二分答案的路径加进去,结果写完之后自己证伪了这个贪心。

如果这题没注意到一个至关重要的点,是很难写下去的:

对于一颗子树,至多存在一条一个端点在这颗子树、另一个端点在另一颗子树的路径。这是由于一颗子树的根节点只有一个父亲,也就是说,子树的根节点到它父亲只存在一条边。

所以我们只要想个办法把某个二分答案下一颗子树内部最多能形成的路径统计出来。这个东西肯定要自底向上得出,显然一颗子树的路径条数取决于它所有子树的路径条数总和,再加上一些经过这颗子树根节点的路径(也可能没有),经过根节点的路径又分为两种情况,一种是终点在另一颗子树种,一种是终点就是根节点。

考虑一个类似树形dp的东西,方便自底向上统计。

我们要统计两个东西:路径的长度、路径的数量。

路径的长度可以推出路径的数量,因此我们只在dp(似乎不是dp)数组记一个穿过子树根节点的路径长度即可。

若要组成一条穿过某颗子树根节点的路径,必然是要合并两条穿过它某两个儿子的路径。我们考虑这样一个贪心:一定是要先合并出尽可能短的满足当前二分答案的路径。所以我们肯定要先拿最短的那条穿过根的路径去匹配另一条尽可能短的、使得它们长度之和满足二分答案的路径。

若要组成一条终点是根节点的路径,我们肯定也是选择那些尽可能短的满足二分答案的路径。所以在合并路径之前,我们先看这个子树是否存在这样的路径,存在就尽早把它统计上。

在上述过程完成后,我们还要选出一条路径穿过根节点,跟其它子树相连(或者仅与父亲节点)。还是那个贪心原则,我们拿出没有统计过的最长的一条路径贡献上去,这样就能使得其它子树中的路径用的尽量少。

说实话我还是不太相信这个贪心是正确的,考场上我更有可能写个搜索。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define ll long long
#define N 500010
using namespace std;
inline int read()
{
int f=1,x=0;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;
}
struct rec{
int next,ver,edge;
}g[N<<1];
int head[N],tot,n,m,res,q[N],tag[N],dp[N];
inline void add(int x,int y,int val)
{
g[++tot].ver=y,g[tot].edge=val;
g[tot].next=head[x],head[x]=tot;
}
inline void check(int x,int fa,int lim)
{
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
if(y==fa) continue;
check(y,x,lim);
}
int tail=0;dp[x]=0;
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver,z=g[i].edge;
if(y==fa) continue;
q[++tail]=dp[y]+z;
}
sort(q+1,q+tail+1);
for(int i=tail;i>=1&&q[i]>=lim;--i)
res--,tail--;
for(int i=1;i<=tail;++i){
if(tag[i]!=x){
int l=i+1,r=tail,pos=tail+1;
while(l<=r){
int mid=(l+r)>>1;
if(q[i]+q[mid]>=lim) r=mid-1,pos=mid;
else l=mid+1;
}
while(tag[pos]==x&&pos<=tail) pos++;
if(pos<=tail) tag[pos]=tag[i]=x,res--;
}
}
for(int i=tail;i>=1;--i)
if(tag[i]!=x){dp[x]=q[i];break;}
}
int main()
{
ll maxn=0;
n=read(),res=m=read();
for(int i=1;i<n;++i){
int u=read(),v=read(),l=read();
add(u,v,l),add(v,u,l);maxn+=l;
}
ll l=1,r=maxn;
while(l<=r){
res=m;
ll mid=(l+r)>>1;
memset(tag,0,sizeof(tag));
check(1,0,mid);
if(res>0) r=mid-1;
else l=mid+1;
}
cout<<l-1<<endl;
return 0;
}

P5021 赛道修建[贪心+二分]的更多相关文章

  1. 二分答案 + multiset || NOIP 2018 D1 T3 || Luogu P5021 赛道修建

    题面:P5021 赛道修建 题解:二分答案,用Dfs进行判断,multiset维护. Dfs(x,fa,Lim)用来计算以x为根的子树中有多少符合条件的路径,并返回剩余未使用的最长路径长. 贪心思想很 ...

  2. 【题解】 P5021赛道修建

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

  3. 洛谷P5021 赛道修建 NOIp2018 贪心+二分答案

    正解:贪心+LCA+二分答案 解题报告: 想先港下部分分qwq因为我部分分只拿到了10ptsQAQ(时间不够不是理由,其实还是太弱,所以要想很久,所以才时间不够QAQ m=1 找直径长度,完 一条链 ...

  4. [NOIP2018]赛道修建(二分+multiset)

    考场上打了一个 \(vector\) 解法,因为我当时不会 \(multiset\) 好吧,我来讲一讲今年的 \(tgD1T3\) 首先,这题 \(55\) 分是不难想的 1. \(b_i=a_i+1 ...

  5. P5021 赛道修建 (NOIP2018)

    传送门 考场上把暴力都打满了,结果文件输入输出写错了.... 当时时间很充裕,如果认真想想正解是可以想出来的.. 问你 长度最小的赛道长度的最大值 显然二分答案 考虑如何判断是否可行 显然对于一个节点 ...

  6. 洛谷P5021 赛道修建

    题目 首先考虑二分,然后发现最小长度越大的话,赛道就越少.所以可以用最终的赛道个数来判断长度是否合理.问题转化为给定一个长度,问最多有多少条互不重叠路径比这个给定长度大. 考虑贪心,毕竟贪心也是二分c ...

  7. [NOIp2018] luogu P5021 赛道修建

    我同学的歌 题目描述 你有一棵树,每条边都有权值 did_idi​.现在要修建 mmm 条赛道,一条赛道是一条连贯的链,且一条边至多出现在一条赛道里.一条赛道的长被定义为,组成这条赛道的边的权值之和. ...

  8. P5021 赛道修建 题解

    原题链接 简要题意: 在一棵树上求 \(m\) 条不相交的路径的最小值的最大值. 本题部分分很多,而且本人也交了 \(27\) 次,所以一定要仔细讲部分分! 算法一 对于 \(b_i = a_i + ...

  9. noip 2018 day1 T3 赛道修建 贪心_树上问题_multiset

    Code: // luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; #define maxn 5000 ...

随机推荐

  1. PatchMatch小详解

    最近发了两片patch match的,其实自己也是有一些一知半解的,找了一篇不知道谁的大论文看了看,又回顾了一下,下面贴我的笔记. The PatchMatch Algorithm patchmatc ...

  2. Python【每日一问】18

    问: [基础题]:请解释新式类跟经典类,并说明它们的区别[提高题]:请解释Python垃圾回收机制 答: [基础题]:请解释新式类跟经典类,并说明它们的区别 1.新式类都是继承内置 object 对象 ...

  3. 词向量实践(gensim)

    词向量训练步骤: 分词并去停用词 词频过滤 训练 项目完整地址:https://github.com/cyandn/practice/tree/master/Word2Vec gensim中Word2 ...

  4. Intellij IDEA 中的 Debug 控制台输出窗口不见了的解决办法

    在 Debug 工具窗口,如图点击左侧重置布局图标,你的console窗口就还原了.

  5. CentOS 7 -防火墙设置--安装数据库,远程连接报错--Can't connect to MySQL server on localhost (10061)

    前提简介:在CentOS 7 上安装了mysql5.7版本,已设置了远程访问权限,但是其他服务器无法访问到此Mysql,提示[Can't connect to MySQL server on loca ...

  6. sql中筛选第一条记录【分组排序】

    问题描述 我们现在有一张表titles,共有4个字段,分别是emp_no(员工编号),title(职位),from_date(起始时间),to_date(结束时间),记录的是员工在某个时间段内职位名称 ...

  7. Java学习:构造方法

    构造方法: 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是再调用构造函数. 格式: public 类名称(参数类型 参数名称){ 方法体 } 注意事项: 构造方法的名称 ...

  8. Java中Integer和ThreadLocal

    一. Integer 1.引子 在开始之前,我还是需要吐槽下自己,我是真的很菜! 他问:**两个Integer对象==比较是否相等? 我答:对象==比较,是引用比较,不相等! 他问:IntegerCa ...

  9. servlet规范--Servlet 规范其实就是对 HTTP 协议做面向对象的封装

    Servlet规范 一个最基本的 Java Web 项目所需的 jar 包只需要一个 servlet-api.jar ,这个 jar 包中的类大部分都是接口,还有一些工具类,共有 2 个包,分别是 j ...

  10. 简明 Java 错误处理机制

    用个最简单的例子来展示为什么需要错误处理,以及怎么用. import java.io.FileInputStream; import java.io.FileNotFoundException; pu ...