题目链接

problem

给出一棵有边权的树。一条链的权值定义为该链所经过的边的边权值和。需要选出\(m\)条链,求\(m\)条链中权值最小的链的权值最大是多少。

solution

首先显然二分。

然后考虑如何判断二分出来的一个答案\(x\)是否是可行的。也就是能否选出\(m\)条链,每条链权值都大于等于\(x\)。这个其实是贪心。

定义直链为从一个某个点的祖先到该点的路径。

可以发现每条链要么就是一条直链,要么由两条直链在某个点处合并起来得到。

贪心的地方在于,对于每个点肯定都是优先将能合成的直链合成。然后再保证向上传递的直链长度最大。因为即便向上传递的长度特别大,产生的贡献也做多只能是\(1\)。所以要先保证在当前子树上合成最多的链。

然后问题就变成了在一棵子树内得到一些直链长度。现在把这些直链两两合并成权值大于等于\(x\)的链。然后保证剩下的直链长度最大。

这里可以二分答案一下。也可以用个\(multiset\)处理。反正是很可做的一个问题。

代码中有各档部分分,BF5为正解

code

#include<set>
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 100010;
ll read() {
ll x = 0,f = 1;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;
}
int n,m;
struct node {
int v,nxt,w;
}e[N << 1];
int head[N],ejs;
void add(int u,int v,int w) {
e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;e[ejs].w = w;
}
namespace BF1 {
int dis[N];
void dfs(int u,int fa) {
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
dis[v] = dis[u] + e[i].w;
dfs(v,u);
}
}
void main() {
dfs(1,0);
int x = 0;
for(int i = 1;i <= n;++i) if(dis[i] > dis[x]) x = i;
// cout<<x<<endl;
memset(dis,0,sizeof(dis));
dfs(x,0);
int ans = 0;
for(int i = 1;i <= n;++i) ans = max(ans,dis[i]);
cout<<ans;
}
} namespace BF2 {
int a[N],cnt;
int check(int x) {
int p = 1,ret = 0;
for(int i = cnt;i > p;--i) {
if(a[i] > x && i > p) {ret++;continue;}
while(a[p] + a[i] < x && p < i) ++p;
if(p < i) ret++,p++;
else break;
}
return ret;
}
void main() {
int l = 100000,r = 0;
for(int i = 1;i <= ejs;i += 2) a[++cnt] = e[i].w,l = min(l,a[cnt]),r += a[cnt];
sort(a + 1,a + cnt + 1); int ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid) >= m) ans = mid,l = mid + 1;
else r = mid - 1;
}
cout<<ans<<endl;
}
}
int du[N];
namespace BF3 { int a[N],cnt;
void dfs(int u,int fa) {
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
a[++cnt] = e[i].w;
dfs(v,u);
}
}
int check(int x) {
int now = 0,ret = 0;
for(int i = 1;i <= cnt;++i) {
now += a[i];
if(now >= x) now = 0,ret ++;
}
return ret;
}
void main() {
for(int i = 1;i <= n;++i)
if(du[i] == 1) {dfs(i,0);break;}
int l = 1000000,r = 0;
for(int i = 1;i <= ejs;i += 2) {
l = min(l,e[i].w);r += e[i].w;
} int ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid) >= m) ans = mid,l = mid + 1;
else r = mid - 1;
}
cout<<ans<<endl; } }
int L = 100000,R;
namespace BF5 {
int ANS;
int dfs(int u,int fa,int x) {
multiset<int>s;
int ret = 0;
// if(!s.empty()) printf("%d\n",u);
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
int k = dfs(v,u,x);
if(k + e[i].w >= x) ANS++;
else s.insert(k + e[i].w);
}
while(!s.empty()) {
multiset<int>::iterator it = s.begin();
int k = *it;
s.erase(it);
multiset<int>::iterator is = s.lower_bound(x - k);
if(is == s.end()) ret = max(ret,k);
else ANS++,s.erase(is);
}
// s.clear(); // printf("%d %d\n",u,ret); return ret;
}
void main() {
int l = L,r = R,ans = 0; while(l <= r) { int mid =(l + r) >> 1;
ANS = 0;dfs(1,0,mid);
if(ANS >= m) ans = mid,l = mid + 1;
else r = mid - 1;
}
cout<<ans;
}
}
int main() {
n = read(),m = read();
int bz1 = 1,bz2 = 1;
for(int i = 1;i < n;++i) {
int u = read(),v = read(),w = read();
L = min(L,w);R += w;
du[u]++;du[v]++; add(u,v,w);add(v,u,w);
if(u != 1) bz1 = 0;
if(v != u + 1) bz2 = 0;
} if(m == 1) {BF1::main();return 0;}
if(bz1) {BF2::main();return 0;}
if(bz2) {BF3::main();return 0;}
BF5::main();
return 0;
}
/*
7 1
1 2 10
1 3 5
2 4 9
2 5 8
3 6 6
3 7 7
*/

Noip2018Day1T3 赛道修建的更多相关文章

  1. Luogu5021 [NOIP2018]赛道修建

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

  2. [NOIp2018提高组]赛道修建

    [NOIp2018提高组]赛道修建 题目大意: 给你一棵\(n(n\le5\times10^4)\)个结点的树,从中找出\(m\)个没有公共边的路径,使得第\(m\)长的路径最长.问第\(m\)长的路 ...

  3. noip2018 D1T3 赛道修建

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

  4. noip 2018 D1T3 赛道修建

    noip 2018 D1T3 赛道修建 首先考虑二分答案,这时需要的就是对于一个长度求出能在树中选出来的最多的路径条数.考虑到一条路径是由一条向上的路径与一条向下的路径构成,或者仅仅是向上或向下的路径 ...

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

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

  6. 【noip2018】【luogu5021】赛道修建

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

  7. 竞赛题解 - NOIP2018 赛道修建

    \(\mathcal {NOIP2018}\) 赛道修建 - 竞赛题解 额--考试的时候大概猜到正解,但是时间不够了,不敢写,就写了骗分QwQ 现在把坑填好了~ 题目 (Copy from 洛谷) 题 ...

  8. [NOIP2018TG]赛道修建

    [NOIP2018TG]赛道修建 考场上multiset调不出啊啊啊!!! 首先肯定是二分答案 做树形dp,f[i]表示i点的子树两两匹配后剩下的最长长度 匹配可以用multiset维护 但是菊花图跑 ...

  9. 【题解】 P5021赛道修建

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

随机推荐

  1. jquery 实现只能选中一个checkbox,选中当前的去除上一个

    jq 实现只能选中一个checkbox,选中当前的去除上一个. <div id="checkboxed"> <input name="check1&qu ...

  2. 一些实用的Django+HTML设置

    一.关于引入变量 1.变量引入方法: {% block 块名称 %} <p>{{变量名}}<p> {% endblock %} 2.引入变量的值中标签是否转义: 不转义: {% ...

  3. MySQL日志简介

    一.MySQL日志简介 二.错误日志 作用: 记录mysql数据库的一般状态信息及报错信息,是我们对于数据库常规报错处理的常用日志. 默认位置: $MYSQL_HOME/data/ 开启方式:(MyS ...

  4. 设置自动获取IP和DNS

    问题阐述 设置ipv4的自动获取时遇到一个问题,ip和dns自动获取可以确认设置,但是全局时就是报错,回头去看ipv4的ip和dns也还是原来的样子 由于一直使用的都是自动获取,很少会有主动设置ip或 ...

  5. oracle 根据时间戳查询date类型sql

    话不多说上sql: select to_char(1574837126879/(1000*60*60*24)+to_date('1970-01-01 08:00:00','YYYY-MM-DD HH2 ...

  6. C# Distinct去重泛型List

    List<int>去重 List<string>去重 List<T>去重 1. List<int>去重 List<int> ilist = ...

  7. 数据结构导论 四 线性表的顺序存储VS链式存储

    前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char nam ...

  8. Appium 1.15.1版本的appium-doctor不是内部或者外部命令的问题

    先讲一下整个app自动化环境的部署过程: 1.安装appium 2.安装nodejs 3.查看appium的环境是否完成 问题:安装appium和nodejs都没啥问题,直接到对应的官网下载然后安装即 ...

  9. java.lang.IllegalStateException: getOutputStream() has already been called 解决办法

    因为在使用的时候没有使用@ResponseBody这个注解,所以才会报上面的异常

  10. 25.md5 collision(NUPT_CTF)

    抓住两点提示: 1.md5碰撞 2.please input a 利用0 == 字符串是成立的,从而可以绕过MD5检查. 所以找一个md5是0e开头的值,因为 php 在处理 == 的时候当碰到的字符 ...