题目描述

一道让人受益匪浅的树形DP+贪心二分题

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

C 城一共有n个路口,这些路口编号为1,2,…,n,有 n-1 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口.其中,第i条道路连接的两个路口编号为ai{a_i}ai​和bi{b_i}bi​,该道路的长度为li{l_i}li​​.借助这n-1条道路,从任何一个路口出发都能到达其他所有的路口。
一条赛道是一组互不相同的道路e1,e2,…,ek{e_1,e_2,…,e_k}e1​,e2​,…,ek​,满足可以从某个路口出发,依次经过 道路e1,e2,…,ek{e_1,e_2,…,e_k}e1​,e2​,…,ek​.

(每条道路经过一次,不允许调头)到达另一个路口.一条赛道的长度等于经过的各道路的长度之和.为保证安全,要求每条道路至多被一条赛道经过。

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

输入格式

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

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

输出格式

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

说明/提示
【输入输出样例 1 说明】

所有路口及适合于修建赛道的道路如下图所示:
道路旁括号内的数字表示道路的编号,非括号内的数字表示道路长度. 需要修建1条赛道.可以修建经过第3,1,2,6条道路的赛道(从路口4到路口7), 则该赛道的长度为 9+10+5+7 = 31,为所有方案中的最大值.

【输入输出样例 2 说明】
所有路口及适合于修建赛道的道路如下图所示:
需要修建3条赛道。可以修建如下3条赛道:
经过第 1,6条道路的赛道(从路口1到7),长度为 6+9=15;
经过第5,2,3,8条道路的赛道(从路口6到9),长度为4+3+5+4=16;
经过第7,4条道路的赛道(从路口8到5),长度为7+10=17.长度最小的赛道长度为15,为所有方案中的最大值。

数据规模与约定

2≤n≤5×104,1≤m≤n−1,1≤ai,bi≤n,1≤li≤104{2 \le n \le 5\times 10^4, \ 1 \le m \le n − 1,\ 1 \le a_i,b_i \le n,\ 1 \le l_i \le 10^4}2≤n≤5×104, 1≤m≤n−1, 1≤ai​,bi​≤n, 1≤li​≤104.

解题思路

本题适合分点解决问题,即先拿部分暴力分,然后逃再思考正解.
这种方法可以较容易地帮助我们在考场上拿到较高得分.

1.bi=ai+1(图为一条链)opts(20分){1.b_i=a_i+1(图为一条链)opts(20分)}1.bi​=ai​+1(图为一条链)opts(20分).
这种情况很简单,即将一个序列分为m个区间,求可以分到的最小区间和的最大值.(经典贪心问题,部分代码如下)时间复杂度:O(nlogn){O(n~logn)}O(n logn)

inline bool isok2(int kk)//核心部分
{int num=0,zhi=0;for(int i=1;i<n;i++){if(zhi+b[i]>=kk){zhi=0;num++;}else zhi+=b[i];if(num>=m)return true;}return false;
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<n;i++){scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].val);b[a[i].x]=a[i].val;//便于写一些,无实质作用.if(a[i].x!=a[i].y-1)tal2=0;}sort(a+1,a+n,cmp1);l=1,r=1e9+7;while(l<r){mi=(l+r+1)>>1;if(isok2(mi))l=mi,ans=mi;else r=mi-1;}printf("%d\n",ans);return 0;
}

2.ai=1(图是菊花图)opts(20分){2.a_i=1(图是菊花图)opts(20分)}2.ai​=1(图是菊花图)opts(20分).
这种情况也很容易处理,可以把问题转化为在一堆数中取m次数,每次取取=一到两个数,使得取出的最小数和最大.(还是一个经典二分,方法就是先将li{l_i}li​从小到大排序,然后用指针在首尾各取一个再让指针逐渐向中间靠拢即可)时间复杂度:O(nlogn){O(n~logn)}O(n logn)

#include<bits/stdc++.h>
#define N 50005
using namespace std;int n,m,tal1=1,tal2=1,ans,l,r,mi;
struct llj{int x,y,val;}a[N];
int b[N];inline bool cmp(const llj &a,const llj &b)
{return a.val<b.val;
}inline bool isok1(int kk)//菊花图核心代码
{int num=0,zhi=0,ll=1,rr=n-1;while(ll<=rr){if(num>=m)return true;zhi=a[rr].val,rr--;if(zhi>=kk){zhi=0;num++;continue;}else{while(zhi+a[ll].val<kk&&ll<=rr+1)ll++;if(ll>rr)return false;ll++;zhi=0;num++;}}if(num>=m)return true;return false;
}inline bool isok2(int kk)
{int num=0,zhi=0;for(int i=1;i<n;i++){if(zhi+b[i]>=kk){zhi=0;num++;}else zhi+=b[i];if(num>=m)return true;}return false;
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<n;i++){scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].val);b[a[i].x]=a[i].val;if(a[i].x!=a[i-1].x&&i>1)tal1=0;//判是否为菊花图if(a[i].x!=a[i].y-1)tal2=0;}if(tal1==1){sort(a+1,a+n,cmp);l=1,r=1e9+7;while(l<r){mi=(l+r+1)>>1;if(isok1(mi))l=mi,ans=mi;else r=mi-1;}}else if(tal2==1)//处理{l=1,r=1e9+7;while(l<r){mi=(l+r+1)>>1;if(isok2(mi))l=mi,ans=mi;else r=mi-1;}}printf("%d\n",ans);return 0;
}

然后40分到手!接下来我们继续向高分迈进.
3.m=1.(求树的直径)opts(15−20分){3.m=1.(求树的直径)opts(15-20分)}3.m=1.(求树的直径)opts(15−20分)
这就是板子(不会点这里),我就不过多的讲述了.(部分代码如下)
时间复杂度:O(n){O(n)}O(n).


inline void lian(int x,int y,int z)
{nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;w[tot]=z;
}inline int Dfs(int u,int fa)//核心部分树形DP
{int sum1=0,sum2=0;for(int i=fi[u];i;i=nxt[i]){int v=to[i];if(v==fa)continue;sum2=max(sum2,Dfs(v,u)+w[i]);if(sum1<sum2)swap(sum1,sum2);}ans=max(ans,sum1+sum2);return sum1;
}int main()
{for(int i=1;i<n;i++){lian(a[i].x,a[i].y,a[i].val);lian(a[i].y,a[i].x,a[i].val);rd[a[i].x]++,rd[a[i].y++];}Dfs(1,0);printf("%d\n",ans);return 0;
}

然后55分暴力就到手了.接下来我们想正解.
容易想到的是用树形DP,同时题面要求的是“最短长度最长”,就是明显的二分答案.
那么我们取min(a[i])为左边界,Σi=1na[i]{\Sigma_{i=1}^{n}a[i]}Σi=1n​a[i]​,a[i]{a[i]}a[i]为右边界进行二分答案.
用两个数组f[i]{f[i]}f[i]和g[i]{g[i]}g[i]分别表示以i为根节点的子树中有多少条长度>=mid,去掉满足条件的路径后从ii往上连最长的长度.
在转移时将当前节点所有子节点的g[i]{g[i]}g[i]加上w[i]{w[i]}w[i]后加入一个multiset中,因为我们之后要先将长度满足>=mid的加到答案中,所以需要从大到小排序,而且不能去重,所以multiset是一个很好的选择.
加入后从后往前扫multiset的元素,如果>=mid加到答案(即f数组中)否则退出循环.
然后我们从前往后扫multiset中的元素,然后用lower_bound在multiset中找能够与它配对的元素,找不到的话就用这个元素来更新g[i]{g[i]}g[i],这样就可以得到最优解了.(AC代码如下)

#include<bits/stdc++.h>
#define N 150005
#define re register int
using namespace std;int n,m,tal1=1,tal2=1,ans,l,r,mi,tot,sum;
struct llj{int x,y,val;}a[N];
int b[N],fi[N],nxt[N],to[N],w[N],f[N],g[N];
multiset<int>s;
multiset<int>::iterator tt;inline bool cmp1(const llj &a,const llj &b)
{return a.val<b.val;
}inline void lian(int x,int y,int z)
{nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;w[tot]=z;
}inline bool isok1(int kk)
{int num=0,zhi=0,ll=1,rr=n-1;while(ll<=rr){if(num>=m)return true;zhi=a[rr].val,rr--;if(zhi>=kk){zhi=0;num++;continue;}else{while(zhi+a[ll].val<kk&&ll<=rr+1)ll++;if(ll>rr)return false;ll++;zhi=0;num++;}}if(num>=m)return true;return false;
}inline bool isok2(int kk)
{int num=0,zhi=0;for(re i=1;i<n;i++){if(zhi+b[i]>=kk){zhi=0;num++;}else zhi+=b[i];if(num>=m)return true;}return false;
}inline void Dfs(int u,int fa)//树形DP核心代码
{for(re i=fi[u];i;i=nxt[i]){int v=to[i];if(v==fa)continue;Dfs(v,u);f[u]+=f[v];}for(re i=fi[u];i;i=nxt[i]){int v=to[i];if(v==fa)continue;s.insert(g[v]+w[i]);}while(!s.empty()){int sum=*s.rbegin();if(sum>=mi){tt=s.find(sum);f[u]++;s.erase(tt);}else break;}while(!s.empty()){int sum=*s.begin();s.erase(s.begin());tt=s.lower_bound(mi-sum);if(tt==s.end())g[u]=sum;else{f[u]++;s.erase(tt);}}
}int main()
{scanf("%d%d",&n,&m);for(re i=1;i<n;i++){scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].val);b[a[i].x]=a[i].val;if(a[i].x!=a[i-1].x&&i>1)tal1=0;if(a[i].x!=a[i].y-1)tal2=0;}if(tal1==1)//保留特判是为了加速{sort(a+1,a+n,cmp1);l=1,r=1e9+7;while(l<r){mi=(l+r+1)>>1;if(isok1(mi))l=mi,ans=mi;else r=mi-1;}}else if(tal2==1){l=1,r=1e9+7;while(l<r){mi=(l+r+1)>>1;if(isok2(mi))l=mi,ans=mi;else r=mi-1;}}if(!tal1&&!tal2){l=1e9+5,r=1;for(re i=1;i<n;i++){lian(a[i].x,a[i].y,a[i].val);lian(a[i].y,a[i].x,a[i].val);l=min(l,a[i].val);r=r+a[i].val;}while(l<r)//核心二分{mi=(l+r+1)>>1;memset(f,0,sizeof(f));memset(g,0,sizeof(g));Dfs(1,0);if(f[1]>=m){l=mi;ans=max(ans,mi);}else r=mi-1;}}printf("%d\n",ans);return 0;
}

完结撒花!!!

NOIP2018D1T3赛道修建相关推荐

  1. NOIP2018·赛道修建

    初见安~本狸参加了2018年的NOIP,然后到现在[看题解]才能过Day1 T3--tcl--QwQ 本篇题解及代码有参考洛谷题解. 本篇夹带了对于二分深切的痛恨.请自行忽略. 传送门:洛谷 P502 ...

  2. 【NOIP 2018 提高组】赛道修建

    传送门 problem C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 mmm 条赛道. C 城一共有 nnn 个路口,这些路口编号为 1,2,-,n1,2,-,n1,2,-,n,有 n−1 ...

  3. P5021 赛道修建

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

  4. 【NOIP2018】赛道修建【二分】【树形dp】【multiset】【贪心】

    传送门 题意:给一棵带边权的树,求MMM条没有公共边的路径的最小长度的最大值. N≤50000N \leq50000N≤50000 抛开NOIP不谈,其实这题本身出的很好 显然二分 问题转化成了&qu ...

  5. noip2018——题解总结

    近期正在疯狂复习某些东西,这篇博客尽量年底更完--(Day2T2除外) 好了,所有的希望都破灭了,原来这就是出题人的素质.--一个被欺骗的可怜 $OIer$ 人生中倒数第三次 $noip$ (Mayb ...

  6. NOIP2018提高组比赛总结

    NOIP2018提高组比赛总结 前言 新赛季,依旧有很多失误. 在些许的遗憾和无奈中,NOIP2018,撒花结束 纵观今年的整一场NOIP,有许多值得总结的地方 正文 NOIP2018初赛 第二次参加 ...

  7. 【NOIP2018】DAY2T2——填数游戏(轮廓线状压的dp?搜索打表)

    描述 小 D 特别喜欢玩游戏.这一天,他在玩一款填数游戏. 这个填数游戏的棋盘是一个n × m的矩形表格.玩家需要在表格的每个格子中填入一个数字(数字 0 或者数字 1),填数时需要满足一些限制. 下 ...

  8. NOIP2018游记题解

    Day 0 openday,open了一天.和yx,zyh三杀 晚上到了学军旁边的酒店. 看了一下电视睡觉了. Day 1 早上8点到了考场,唯一的感觉是冷. 8点15分进了考场. 700+台笔记本. ...

  9. 2018 NOIP 提高组 复赛 day1

    文章目录 T1 铺设道路 T1 分析 T2 货币系统 T2 分析 T3 赛道修建 T3 分析 T1 铺设道路 题目点击→洛谷 P5019 铺设道路 题目描述 春春是一名道路工程师,负责铺设一条长度为 ...

最新文章

  1. IDEA 在线翻译插件
  2. Python的可视化包 – Matplotlib 2D图表(点图和线图,.柱状或饼状类型的图),3D图表(曲面图,散点图和柱状图)...
  3. boost::fusion::result_of::size用法的测试程序
  4. 第5章 Python 数字图像处理(DIP) - 图像复原与重建13 - 空间滤波 - 线性位置不变退化 - 退化函数估计、运动模糊函数
  5. python 运维自动化之路 Day2
  6. 10.数据库中的内置函数
  7. python 扩展_用Python方法对Cursor进行扩展
  8. kettle升级jetty10实验(未完成jaas认证)
  9. 在Linux系统上安装Spring boot应用
  10. SQL各个关键字的顺序
  11. servlet使用jsp内置对象
  12. 项目管理高手常用的10张图表推荐!(小白也能懂的项目管理)
  13. 【全网世界区划最全整理输出之第一部分】全世界所有国家的行政区划整理,省市信息,已按照国家,省,市排好序,可直接复制使用,第一部分4006条,总条数:21088
  14. 360开源的插件化框架Replugin深度剖析
  15. 关于IDEA的一些常用的快捷键整合,赶紧进来KK......
  16. dnf 台服服务器pvf修改器,求教怎么用pvf修改器自制装备
  17. FireStart教程:基于SharePoint的出差报销流程二
  18. 电子书管理软件Calibre使用
  19. css3探测光圈_CSS3光圈散开提示效果
  20. 华为路由器:WLAN直连式三层组网实验

热门文章

  1. 金融工程---马尔科夫预测
  2. 欢乐庆团圆--记华清远见嵌入式学院老学员联谊会
  3. php 如何生成noncestr,如何创建和使用nonce
  4. 结构图的分类--产品功能结构图、产品信息结构图、产品结构图
  5. OpenCV 书稿:前言
  6. 亚马逊云服务器使用密码登录
  7. Windows保护模式(三)长调用与短调用调用门
  8. 函数——IIFE、作用域、函数调用、函数应用、闭包
  9. pytest_bdd.exceptions.FeatureError: Step definition outside of a Scenario or a Background.
  10. ubuntu16.04下面安装搜狗中文输入法