题目描述

给定一个 \(N\) 个点的树,要选出一条所含点的个数不超过 \(K\) 的一条路径,使得路径外的点到这条路径的距离的最大值最小。

数据范围:\(1\le K \le N \le 10^5\)

解题思路

这道题我有两种方法。

方法一

我们考虑一个性质:选出来的链一定会是直径的一部分

不然就肯定会存在可能更新最大值的一个分支,而且这个分支的大小一定会不比路径包含在直径上时小。

同样的道理,我们发现这条路径在直径上越长越好。

那么我们不妨先把直径抠出来,记作一个序列,那么这颗树就可以想象成一条链挂了很多子树。

我们dfs出每一个直径上的点,它下面的子树的以根为源点的最长路,然后用 \(\text{ST}\) 表维护一下它的区间最大值,再加上一点前缀和,然后在直径上移动这个序列,更新答案就好了。

复杂度是 \(O(n \log n)\) 的。

方法二

考虑一种贪心的思想。

我们考虑不断删掉叶子,每次选择距离目标形态最近的叶子删掉,并且判断一下他的父亲是不是成了新的叶子,并将他的答案向父亲合并。

关于正确性:

  1. 删成链之后就会继续删链的端点,这肯定没问题。
  2. 这里的最近类似与上文中的“直径上的点的子树的以根为源点的最长路”,所以是对的。
  3. 由于选择顺序的规定,所以更新父亲的一定是最后被选的一个儿子,因为它的关键字最大,所以只需要在父亲变叶子时更新。

知道了原理后,我们可以用 set 存边,方便进行删边操作,用 priority_queue 来维护所有的叶子,然后直接模拟这个过程就好了。

细节注意事项

  • 用第一种方法会稍微有一点难调。
  • 用第二种方法就可能会出现 STL 的使用出错等问题。

参考代码

这是方法一的代码,由于我自己没(lan)有(de)打,所以这里放的是 \(\text{\color{black}{M}\color{red}{\_sea}}\) 神仙的代码

// ===================================
// author: M_sea
// website: http://m-sea-blog.com/
// ===================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define re register
using namespace std; inline int read() {
int X=0,w=1; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
return X*w;
} const int N=100000+10; int n,k; struct edge { int v,w,nxt; } e[N<<1];
int head[N];
inline void addEdge(int u,int v,int w) {
static int cnt=0;
e[++cnt]=(edge){v,w,head[u]},head[u]=cnt;
} int rt1,rt2;
int dis[N],fa[N];
inline void dfs1(int u,int f) {
if (dis[u]>dis[rt1]) rt1=u;
for (re int i=head[u];i;i=e[i].nxt)
if (e[i].v!=f) dis[e[i].v]=dis[u]+e[i].w,dfs1(e[i].v,u);
}
inline void dfs2(int u,int f) {
fa[u]=f;
if (dis[u]>dis[rt2]) rt2=u;
for (re int i=head[u];i;i=e[i].nxt)
if (e[i].v!=f) dis[e[i].v]=dis[u]+e[i].w,dfs2(e[i].v,u);
} int cnt=0,lend=0;
int in[N],maxdis[N],w[N];
int lg[N],st[17][N],sum[N];
inline void dfs3(int u,int f) {
for (re int i=head[u];i;i=e[i].nxt) {
int v=e[i].v; if (v==f) continue;
w[v]=e[i].w,dfs3(v,u);
if (!in[v]) maxdis[u]=max(maxdis[u],maxdis[v]+e[i].w);
}
}
inline void init_diameter() {
for (re int i=rt2;i;i=fa[i]) in[i]=1,++cnt;
dfs3(rt1,0);
for (re int i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
for (re int i=rt2,j=1;i;i=fa[i],++j) st[0][j]=maxdis[i];
for (re int i=1;i<17;++i)
for (re int j=1;j+(1<<i)-1<=cnt;++j)
st[i][j]=max(st[i-1][j],st[i-1][j+(1<<(i-1))]);
for (re int i=rt2,j=1;i;i=fa[i],++j) sum[j]=w[i],lend+=w[i];
for (re int i=1;i<=cnt;++i) sum[i]+=sum[i-1];
}
inline int query(int l,int r) {
int t=lg[r-l+1];
return max(st[t][l],st[t][r-(1<<t)+1]);
} int main() {
n=read(),k=read();
if (n==1) { puts("0"); return 0; }
for (re int i=1;i<n;++i) {
int u=read(),v=read(),w=read();
addEdge(u,v,w),addEdge(v,u,w);
}
dfs1(1,0),dfs2(rt1,0);
init_diameter();
if (k>cnt) printf("%d\n",query(1,cnt));
else {
int ans=2e9;
for (re int i=1;i+k-1<=cnt;++i) {
int tmp=max(sum[i-1],lend-sum[i+k-2]);
ans=min(ans,max(tmp,query(i,i+k-1)));
}
printf("%d\n",ans);
}
return 0;
}

下面是方法二的代码,我自己敲的。

/*
================================
Author: The Ace Bee
Blog: www.cnblogs.com/zsbzsb
This code is made by The Ace Bee
================================
*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
#include <queue>
#include <set>
#define rg register
using namespace std;
template < typename T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while (!isdigit(c)) f |= (c == '-'), c = getchar();
while (isdigit(c)) s = s * 10 + (c ^ 48), c = getchar();
s = f ? -s : s;
} const int _ = 100000 + 10; int n, k, ans;
struct node{ int u, w; };
inline bool operator < (const node& x, const node& y) { return x.w > y.w; }
priority_queue < node > Q;
set < pair < int, int > > s[_]; int main() {
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
#endif
read(n), read(k);
for (rg int u, v, d, i = 1; i < n; ++i) {
read(u), read(v), read(d);
s[u].insert(make_pair(v, d));
s[v].insert(make_pair(u, d));
}
for (rg int i = 1; i <= n; ++i)
if (s[i].size() == 1) Q.push((node) { i, (*s[i].begin()).second });
while (Q.size() > 2 || k < n) {
node x = Q.top(); Q.pop(), --n, ans = x.w;
int u = x.u, v = (*s[u].begin()).first;
s[v].erase(s[v].lower_bound(make_pair(u, 0)));
if (s[v].size() == 1) Q.push((node) { v, ans + (*s[v].begin()).second });
}
printf("%d\n", ans);
return 0;
}

完结撒花 \(qwq\)

「CF1004E」Sonya and Ice Cream的更多相关文章

  1. E. Sonya and Ice Cream(开拓思维)

    E. Sonya and Ice Cream time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  2. Sonya and Ice Cream CodeForces - 1004E 树的直径, 贪心

    题目链接 set维护最小值贪心, 刚开始用树的直径+单调队列没调出来... #include <iostream>#include <cstdio> #include < ...

  3. CodeForces - 1004E Sonya and Ice Cream

    题面在这里! 挺智障的一个二分...我还写了好久QWQ,退役算啦 题解见注释... /* 先对每个点记录 向子树外的最长路 和 向子树内最长路,然后二分. 二分的时候枚举链的LCA直接做就好啦. */ ...

  4. Codeforces #495 Div2 problem E. Sonya and Ice Cream(1004E)

    网上的大多是用树的直径做的,但是一些比较巧妙的做法,来自https://www.cnblogs.com/qldabiaoge/p/9315722.html. 首先用set数组维护每一个节点所连接的边的 ...

  5. 「译」JUnit 5 系列:条件测试

    原文地址:http://blog.codefx.org/libraries/junit-5-conditions/ 原文日期:08, May, 2016 译文首发:Linesh 的博客:「译」JUni ...

  6. 「译」JUnit 5 系列:扩展模型(Extension Model)

    原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...

  7. JavaScript OOP 之「创建对象」

    工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程.工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题. function createPers ...

  8. 「C++」理解智能指针

    维基百科上面对于「智能指针」是这样描述的: 智能指针(英语:Smart pointer)是一种抽象的数据类型.在程序设计中,它通常是经由类型模板(class template)来实做,借由模板(tem ...

  9. 「JavaScript」四种跨域方式详解

    超详细并且带 Demo 的 JavaScript 跨域指南来了! 本文基于你了解 JavaScript 的同源策略,并且了解使用跨域跨域的理由. 1. JSONP 首先要介绍的跨域方法必然是 JSON ...

随机推荐

  1. map遍历删除

    List<Object> orderManageList = cacheService.values(key); Iterator<Object> it=orderManage ...

  2. C语言与汇编的嵌入式编程:求100以内素数

    写汇编之前,需要搞清楚C语言代码的写法,这里以最简单的算法举例说明 C代码如下: #include <stdio.h> void main(){ int i,j; ; ;i<=;i+ ...

  3. Java补强转

    /* 对于byte/short/char三种类型来说,如果右侧赋值的数值没有超过范围, 那么javac编译器将会自动隐含地为我们补上一个(byte)(short)(char). 1. 如果没有超过左侧 ...

  4. 【译】高级T-SQL进阶系列 (三)【中篇】:理解公共表表达式(CTEs)

    [译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正] 原文链接:传送门. 一个简单的CTE例子 如前所述,CTE‘s提供给你了一个方法来更容易的书写复杂的代码以提高其可读性.假设你有列表 ...

  5. PCC值average pearson correlation coefficient计算方法

    1.先找到task paradise 的m1-m6: 2.根据公式Dy=D1* 1/P*∑aT ,例如 D :t*k1   a:k2*k1: Dy :t*k2 Dy应该有k2个原子,维度是t: 3.依 ...

  6. 01 认识python

    python介绍 python的创始⼈为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决⼼ 开发⼀个新的脚本解释程序,作为ABC语 ...

  7. Steam 游戏 《Sudoku Universe(数独宇宙)》、《Sudoku Killer(数独杀手)》、《Sudoku Jigsaw(数独拼图)》数字位置解析 ---------C# 数独程序解析(2020年寒假小目标11)

    日期:2020.02.11 博客期:151 星期二 今天,准备肝一个 C# 的数独读写工具(汇编语言也在努力学习命令方法中...),这三个游戏我早就买下了,一直放在 Steam 库里积灰,看着它的成就 ...

  8. Python爬虫:requests模块的基本使用

    官方文档:https://requests.readthedocs.io/zh_CN/latest/ 基本使用 基本结构 发送请求,获取响应:r = requests.get(url) 爬取网页的通用 ...

  9. JavaSE复习~开发环境的搭建 与 HelloWorld

    JDK的下载 访问Oracle官网,下载jdk,目前来说用的最多的是 8 版本 https://www.oracle.com/technetwork/java/javase/downloads/ind ...

  10. Spring Boot 2.0 新特性

    这是一篇总结文章,主要收集 Spring Boot 2.0 相对于 Spring Boot 1.x 的新特性,本章节并不提供实践性质的源代码.在 Spring Boot 系列文章中会持续退出实践章节. ...