题目描述

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;
}

最新文章

  1. 爆一个VS2015 Update1更新带来的编译BUG【已有解决方案】
  2. Bash脚本实现批量作业并行化
  3. 便捷的php操作mysql库MysqliDb
  4. zw版【转发&#183;台湾nvp系列例程】HALCON MirrorRegion (Delphi)
  5. 如何在远程计算机上运行PowerShell
  6. 执行asp.net上传下载Excel时出现“未在本地计算机上注册“Microsoft.ACE.Oledb.12.0”提供程序。(System.Data)”错误
  7. oracle中查看某个用户名下所有的表以及sequence
  8. php--常用的时间处理函数
  9. MySql数据库3【优化2】sql语句的优化
  10. Swift 断言
  11. InstallShield集成安装MSDE2000最小版本(三) fishout特许授权发布
  12. Hibernate注解-类级别注解
  13. post请求测试
  14. translate.py
  15. 【Codeforces Round 1117】Educational Round 60
  16. jQuery实现多个ajax请求等待
  17. 20172301 2017-2018-2 《程序设计与数据结构》实验一《Java开发环境的熟悉》实验报告
  18. 总结MySQL修改最大连接数的两个方式
  19. inline函数的总结
  20. libgdx与Robovm绑定的坑

热门文章

  1. Tomcat通过脚本自动部署
  2. SVN与GIT工具使用对比
  3. IO流—其他流
  4. SSL密钥协商过程分析
  5. SQL Server 10分钟理解游标
  6. php redis扩展安装步骤
  7. Neo4j学习——基本操作(一)
  8. - instanceof 和 isInstance 强转 类型 class MD
  9. C#读写修改设置调整UVC摄像头画面-色调
  10. WindowsXP序列号产生原理(椭圆曲线法)