\(\mathcal {NOIP2018}\) 赛道修建 - 竞赛题解

额……考试的时候大概猜到正解,但是时间不够了,不敢写,就写了骗分QwQ

现在把坑填好了~


题目

(Copy from 洛谷)

题目描述

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\) 条赛道中最短赛道的长度尽可能大)

输入输出格式

输入格式:

输入文件第一行包含两个由空格分隔的正整数 \(n,m\),分别表示路口数及需要修建的 赛道数。

接下来 \(n-1\) 行,第 ii 行包含三个正整数 \(a_i,b_i,l_i\) ,表示第 \(i\) 条适合于修建赛道的道 路连接的两个路口编号及道路长度。保证任意两个路口均可通过这 \(n-1\) 条道路相互到达。每行中相邻两数之间均由一个空格分隔。

输出格式:

输出共一行,包含一个整数,表示长度最小的赛道长度的最大值。


解析

首先题目非常重要——这是一个树形结构,而且满足单调性(即如果答案为x,则>x的值都不能符合条件,<x的值都符合条件)。

那么根据单调性就可以二分了,还是比较容易想到二分答案。然后就是检验的问题了……

如果大家做了“菊花图”(就是从一个顶点伸出去很多条边)的部分数据,就应该知道有一个贪心的性质——对于一个点u,如果连接 u 和 u的一个儿子 的边的长度大于等于答案,那就把这条边割开,单独形成一条路径;接下来考虑长度比答案小的边,那么我们尽量地将长度大的边与较小边匹配形成一条路径。这样的话我们就可以写出来一段代码(AC的):

//edg[]是u与u的儿子的边的长度(都不超过二分出来的答案)
sort(edg,edg+n);
int res=0;
for(int l=0,r=n;l<r;r--){
while(l<r && edg[l]+edg[r]<ans) l++;
if(l>=r) break;
res++; //新构成的路径
l++;
}

我们不妨考虑每一个点 u 及其儿子 v[] ——形成路径的情况无非是①加上u->v[i]的边形成一个路径;②加上u->v[i]和u->v[j]的边形成一个路径;③加上u->v[i]的边然后再与u的祖先相连。

不难想到我们可以把每棵子树都看成一个菊花图做一次贪心。

但是有上述的情况③,这样的话就不能直接看边权了,我们可以让 dp[u] 表示在点u的子树以u为端点的路径的长度。然后考虑子树u中u的一个儿子v,如果 dp[v]+(u->v的边长) 满足答案,就将已经构成的路径个数cnt++;否则将 dp[v]+(u->v的边长) 储存到use[u]中。最后把use[u]中的边按菊花图的方法做一次贪心,就可以得到能够构成的路径条数的最大值:

for(int l=0,r=use[u].size()-1;r>=0;r--){
while(l<r && use[u][l]+use[u][r]<num) l++;
if(l>=r) break;
mat++;l++;
}
fans[u]+=mat; //储存答案

贪心的想,在这里一定要尽可能多的构成路径,但是在这个条件下将尽可能长的一个没必要匹配的路径留下来继续向u的祖先连接;很明显这也是存在单调性的——如果选择一条长度为X的路径“留下来”可以得到最大构成的路径数,那么长度小于X的路径就一定也可以得到最大路径数。

while(l+1<r){
int mid=(l+r)>>1;
if(Check(u,mid,use[u].size(),num)==mat) l=mid;
else r=mid;
}
dp[u]=use[u][l];

这样就可以检验了,即看能够得到的路径条数是否能够满足要求。

(不知道大家看懂没啊……有什么问题的话还是直接在文末的作者邮箱里面问吧QwQ)


源代码

/*Lucky_Glass*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=50000;
struct EDGE{
int to,nxt;ll dis;
EDGE(){}
EDGE(int _to,int _nxt,ll _dis):to(_to),nxt(_nxt),dis(_dis){}
}edg[N*2+5];
int adj[N+5],edgtot;
void AddEdge(int u,int v,ll dis){
edg[++edgtot]=EDGE(v,adj[u],dis);
adj[u]=edgtot;
}
int n,m;
vector< int > use[N+5];
int dp[N+5],fans[N+5];
int Check(int u,int tag,int siz,ll num){
int res=0;
for(int l=0,r=siz-1;r;r--){
if(r==tag) r--;
while(l<r && use[u][l]+use[u][r]<num) l++;
if(l==tag) l++;
if(l>=r) break;
res++;l++;
}
return res;
}
void DFS(int u,int pre,ll num){
dp[u]=fans[u]=0;use[u].clear();
for(int i=adj[u];i;i=edg[i].nxt){
int v=edg[i].to;ll dis=edg[i].dis;
if(v==pre) continue;
DFS(v,u,num);
dp[v]+=dis;
if(dp[v]>=num) fans[u]++;
else use[u].push_back(dp[v]);
}
sort(use[u].begin(),use[u].end());
int mat=0;
for(int l=0,r=use[u].size()-1;r>=0;r--){
while(l<r && use[u][l]+use[u][r]<num) l++;
if(l>=r) break;
mat++;l++;
}
fans[u]+=mat;
if(mat*2==use[u].size()) return;
int l=0,r=use[u].size();
while(l+1<r){
int mid=(l+r)>>1;
if(Check(u,mid,use[u].size(),num)==mat) l=mid;
else r=mid;
}
dp[u]=use[u][l];
}
bool Check(ll num){
DFS(1,-1,num);
int res=0;
for(int i=1;i<=n;i++)
res+=fans[i];
return res>=m;
}
int main(){
scanf("%d%d",&n,&m);
ll l=0,r=0;
for(int i=1;i<n;i++){
int u,v;ll dis;scanf("%d%d%lld",&u,&v,&dis);
AddEdge(u,v,dis);AddEdge(v,u,dis);
r+=dis;
}
r/=1ll*m;
while(l+1<r){
ll mid=(l+r)>>1;
if(Check(mid)) l=mid;
else r=mid;
}
if(Check(r)) printf("%lld\n",r);
else printf("%lld\n",l);
return 0;
}

\(\mathcal{THE\ END}\)

\(\mathcal{Thanks\ for\ reading!}\)

\(\mathcal{email:\ lucky\_glass@foxmail.com}\ 尽量在平时回答一下问题QwQ\)

竞赛题解 - NOIP2018 赛道修建的更多相关文章

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

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

  2. Luogu5021 [NOIP2018]赛道修建

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

  3. 竞赛题解 - NOIP2018 保卫王国

    \(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...

  4. 竞赛题解 - NOIP2018 旅行

    \(\mathcal {NOIP2018} 旅行 - 竞赛题解\) 坑还得一层一层的填 填到Day2T1了 洛谷 P5022 题目 (以下copy自洛谷,有删减/修改 (●ˇ∀ˇ●)) 题目描述 小 ...

  5. 【题解】NOIP2018 赛道修建

    题目戳我 \(\text{Solution:}\) 根据题目信息简化题意,是让你在树上找出\(m\)条路径使得路径长度最小值最大. 看到题第一感先二分一个答案,问题转化为如何选择一些路径使得它们最小值 ...

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

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

  7. 【比赛】NOIP2018 赛道修建

    最小值最大,二分长度 然后判断赛道大于等于这个长度最多可以有多少条 可以贪心,对于一个点和它的一些儿子,儿子与儿子之间尽量多配(排序后一大一小),剩下的选个最长的留给自己的父亲就好了 具体实现可以用一 ...

  8. [NOIP2018]赛道修建

    嘟嘟嘟 因为一些知道的人所知道的,不知道的人所不知道的原因,我来改写今年的NOIP了. 现在看这题,心中满是疑问:我当时是多么的zz,这种水题为啥没做出来-- 不管了,说正事. 先考虑部分分. 1.\ ...

  9. luogu5021 [NOIp2018]赛道修建 (二分答案+dp(贪心?))

    首先二分一下答案,就变成了找长度>=m的 不相交的路径的个数 考虑到在一个子树中,只有一个点能出这个子树去和别的点搞 所以我这个子树里尽量自我满足是不会有坏处的 而且要在自我满足数最大的条件下, ...

随机推荐

  1. 学习笔记flexbox新布局

    FlexBox简称“弹性盒子”,除了用于实现弹性布局,还可以用来居中内容,改变标记中的源码顺序.首先说明IE9及以下浏览器不支持FlexBox. .flex{ display:flex; flex:1 ...

  2. Java集合排序

    [ 1.对普通的包装类基本数据类型的list数组排序(Integer,Long,Double) ] Collections.sort(List list) [例] List<Long> m ...

  3. JAVA的静态方法,静态变量,静态类。

    静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. (1)Java静态对象和非静态对象有什么区别? 比对如下: 静态对象                                ...

  4. velecity报错:Caused by: org.apache.velocity.exception.ParseErrorException: Lexical error, Encountered: <EOF> after : "\'/order/pay?activity=\" + activityId);\r\n }*/\r\n</script>\r\n#end\r\n" at /a

    Caused by: org.apache.velocity.exception.ParseErrorException: Lexical error, Encountered: <EOF> ...

  5. Less的guards and argument matching

    less guards/argument matching: .setbackground(@number) when (@number>0){ .setbackground( @number ...

  6. 微软在Build 2016开发者大会中发布 “认知服务”,牛津计划有正式名字啦!

    2016年3月30日:微软在Build 2016开发者大会中发布“认知服务”. 在Build 2016开发者大会中,微软发布了新的智能服务:微软认知服务(Microsoft Cognitive Ser ...

  7. Azkaban调度器

    Azkaban介绍 Azkaban 是由 Linkedin 公司推出的一个批量工作流任务调度器,用于在一个工作流内以一个特定的顺序运行一组工作和流程.Azkaban 使用 job 配置文件建立任务之间 ...

  8. 关于cmake输出动态链接库名字的问题

    使用cmake进行项目编译管理时,我们经常使用 add_library(foo SHARED foo.cpp) 这样的话,输出时,如果在win下面会得到foo.dll,linux下面会得到libfoo ...

  9. C语言main函数的参数

    在Windows下使用gcc编译器: 1.首先介绍下MinGW MinGW(Minimalist GNU for Windows),又称mingw32,是将GCC编译器和GNU Binutils移植到 ...

  10. Map使用方法

    转:https://www.cnblogs.com/lzq198754/p/5780165.html Java map 详解 - 用法.遍历.排序.常用API等 概要: java.util 中的集合类 ...