\(\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. easyui扩展行默认展开 以及 去除滚动条

     问题背景: 在做打印页面的时候,要求有详细的默认展开显示.    遇到的问题: 1)在用扩展行的时候,grid的所有行都添加了展开收起的图标,(第二行没有明细)如下  2)默认展示有详细行的时候,内 ...

  2. JS判断是否到达页面底部

    <script type="text/javascript">//判断整个文档到底部$(window).scroll(function(){    //滚动条所在位置的 ...

  3. 回溯法最优装载问题(java)

    1.问题描述:      有一批共有 n 个集装箱要装上两艘载重量分别为 c1 和 c2 的轮船,其中集装箱 i 的重量为 w[i], 且重量之和小于(c1 + c2).装载问题要求确定是否存在一个合 ...

  4. string 替换字符串

    C++的string提供了replace方法来实现字符串的替换,但是对于将字符串中某个字符串全部替换这个功能,string并没有实现,我们今天来做的就是这件事.首先明白一个概念,即string替换所有 ...

  5. How I explained OOD to my wife

    Introduction My wife Farhana wants to resume her career as a software developer (she started her car ...

  6. 【Leetcode】【Medium】Unique Binary Search Trees

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...

  7. Java SpringMVC学习--基础配置

    快速开始一个基于SpringMVC框架的web项目 开发工具 Eclipse neon.2 运行环境 tomcat8.5 1.在Eclipse中新建一个web项目:File-New-Dynamic W ...

  8. 32位 64位 获得进程peb的方法

    基于上一篇文章,大概了解了peb的获取方法,但是那个方法只能获得当前进程的PEB,不能获得其他的进程的PEB.根据那个思想,获得其他进程PEB则需要注入,得到进程信息,然后进程间通信,将信息返回来,经 ...

  9. Python迭代(入门8)

    转载请标明出处: http://www.cnblogs.com/why168888/p/6407980.html 本文出自:[Edwin博客园] Python迭代 1. 什么是迭代 注意: 集合是指包 ...

  10. phonegap 的指南针 api Compass

    一. Compass 介绍方法参数   1.Compass 也就是,常说的指南针,又叫罗盘 2.方法 compass.getCurrentHeading compass.watchHeading co ...