P2491 消防/P1099 树网的核

双倍经验,双倍快乐。

题意

在一个树上选择一段总长度不超过\(s\)的链使所有点到该链距离的最大值最小。

输出这个最小的值。

做法

Define:以下\(s\)指链或链长。

  1. 证明一下\(s\)一定处于直径上。假设它不在直径上,一定存在直径的其中一个端点到\(s\)的距离大于现在所处支链的最大距离。所以\(s\)不在直径上一定不优。
  2. 于是我们找到直径并记录下直径上的所有点。
  3. 然后,我们枚举直径上的每一个长度小于\(s\)的最长区间(最长原因显然,因为长度越短答案肯定不会更优),并计算此时的答案,对于每一个区间的答案取min即可。
  4. 考虑计算每个区间的答案。我们把直径拉出来,用两个指针\(l\)和\(r\)从左向右遍历这个直径,\(s\)即为\(l\)到\(r\)。考虑此时这个区间的答案即为\(l\)到\(r\)中每个点\(i\)的子树中最深的点的距离(我们设为\(h_i\))(注意这里的子树是不包括直径的,即子树中所有的点都属于支链)和\(l\)到直径左端点的距离(设为\(ls\))和\(r\)到直径右端点的距离(设为\(rt\))的最大值。原因显然。
  5. 那么我们可以预处理出\(h_i\),并在遍历直径的时候用单调队列维护\(h\)的最大值,然后用这个值与\(ls\)和\(rt\)的最大值更新答案(取最小值)即可。

一些疑问

  • 当存在多条直径时,区间似乎一定包括重心并尽量使重心居中。然而这并没有什么卵用,并且一样可以用上面的方法做,不会造成影响。

  • \(s\)的左右两端一定在端点上。既是,\(s\)是可以为一个点的。

具体实现和代码

  • 求直径时两次DFS即可。然后发现记录的d数组刚好可以用来当做前缀和(只是使代码略显凌乱罢了)
  • 单调队列似乎要特殊记录一下链首的位置而不能用head代替,否则无法记录区间长(或者只是我没有想到更好的处理方法)
  • 求深度DFS或BFS.
  • 时间复杂度O(n).

(学会了一个新单词diameter,意思是直径,重音在|a|上)

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read(){
int x=0,w=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=3e5+10;
int n,l;
int ecnt,head[maxn],nxt[maxn<<1],to[maxn<<1],dis[maxn<<1];
inline void addedge(int a,int b,int c){
to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt;dis[ecnt]=c;
to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt;dis[ecnt]=c;
}
int d[maxn],mx,fa[maxn],diam[maxn],tot,sum;
void dfs1(int x,int f,int &dia){
fa[x]=f;
if(mx<d[x])
mx=d[x],dia=x;
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==f)continue;
d[u]=d[x]+dis[i];
dfs1(u,x,dia);
}
}
inline void diameter(){
int dia,dia2;
dfs1(1,0,dia);
mx=0;d[dia]=0;
dfs1(dia,0,dia2);
while(dia2!=dia){
diam[++tot]=dia2;
dia2=fa[dia2];
}
diam[++tot]=dia;
}
int h[maxn],dep[maxn],q[maxn],ans=0x3f3f3f3f;
void dfs2(int x,int f){
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==f)continue;
dfs2(u,x);
dep[x]=max(dep[x],dep[u]+dis[i]);
}
}
inline void solve(){
for(int i=2;i<tot;i++){
int x=diam[i];
h[i]=0;dep[x]=0;
for(int j=head[x];j;j=nxt[j]){
int u=to[j];
if(u==diam[i-1] or u==diam[i+1])continue;
dfs2(u,x);
h[i]=max(h[i],dep[u]+dis[j]);
}
}
int s=1,t=0,ls=0,rt=mx,from=1;
for(int i=1;i<=tot;i++){
while(s<=t and d[diam[from]]-d[diam[i]]>l)from++,s+=(from>q[s]),ls=(mx-d[diam[from]]);
while(s<=t and h[i]>h[q[t]])t--;
q[++t]=i;rt=d[diam[i]];
ans=min(ans,max(h[q[s]],max(ls,rt)));
}
printf("%d",ans);
}
inline void work(){
n=read(),l=read();
for(int a,b,c,i=1;i<n;i++)a=read(),b=read(),c=read(),addedge(a,b,c);
diameter();
solve();
}
}
signed main(){
star::work();
return 0;
}

P2491 消防/P1099 树网的核的更多相关文章

  1. 洛谷 P1099 树网的核

    P1099 树网的核 题目描述 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W ...

  2. bzoj1999 / P1099 树网的核

    P1099 树网的核 (bzoj数据加强) 前置知识:树的直径 (并不想贴我的智障写法虽然快1倍但内存占用极大甚至在bzoj上MLE) 正常写法之一:用常规方法找到树的直径,在直径上用尺取法找一遍,再 ...

  3. P1099 树网的核——模拟+树形结构

    P1099 树网的核 无根树,在直径上找到一条长度不超过s的路径,使得最远的点距离这条路径的距离最短: 首先两遍dfs找到直径(第二次找的时候一定要吧father[]清零) 在找到的直径下枚举长度不超 ...

  4. [SDOI2011]消防/[NOIP2007] 树网的核

    消防 题目描述 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的 ...

  5. [NOIP2007] 提高组 洛谷P1099 树网的核

    题目描述 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W表示各边长度的集合,并 ...

  6. P1099 树网的核 && P2491 [SDOI2011]消防

    给定一棵树, 你可以在树的直径上确定一条长度不超过 \(S\) 的链, 使得树上离此链最长的点距离最小, 输出这个距离 P2491 数据范围为 P1099 的 \(1000\) 倍 Solution ...

  7. 洛谷 P1099 树网的核+P2491 [SDOI2011]消防

    写在前面:由于是双倍经验就放一块了,虽然数据范围差的有点大. 题目链接 题意:在树的直径上选择一条长度不超过s的路径使这条路径上的点到树上任意点的最大距离最小. 这题数据好像非常水,我写了上界n^2不 ...

  8. #P1099 树网的核 题解

    题目描述 pdf 题解 这一题,刚开始看题目感觉好像很难,题目又长……一看数据范围,呵呵. 已经给出来这是个DAG,所以不用担心连通性的问题.那么怎么做呢? 朴素的做法是把树的直径的两个端点都统计出来 ...

  9. BZOJ2282 SDOI2011消防/NOIP2007树网的核(二分答案+树形dp)

    要求最大值最小容易想到二分答案.首先对每个点求出子树中与其最远的距离是多少,二分答案后就可以标记上一些必须在所选择路径中的点,并且这些点是不应存在祖先关系的.那么如果剩下的点数量>=3,显然该答 ...

随机推荐

  1. MySQL必知必会笔记——查询的基础知识

    查询基础知识 第七章 数据过滤 组合where子句 MySQL允许给出多个WHERE子句.这些子 句可以两种方式使用:以AND子句的方式或OR子句的方式使用. AND操作符 可使用AND操作符给WHE ...

  2. vulhub-struts2-s2-007

    0x00 漏洞原理   当配置了验证规则 <ActionName>-validation.xml 时,若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次 OGNL ...

  3. .Net Redis实战指南——常用命令

    本问主要介绍rabbitmqctl工具的常用命令. vhost 一个RabbitMQ服务器可以创建多个虚拟的消息服务器,称之为虚拟主机(virtual host),简称为vhost.vhost之间是绝 ...

  4. ipconfig提示不是内部或外部命令

    昨天因为公司断网,重新连上之后ip地址变了,于是就想看看现在的ip是什么 输入ipconfig,回车 提示不是外部和内部命令,是因为系统在本路径下未找到ipconfig.exe系统,所以无法识别ipc ...

  5. SystemVerilog 中的相等运算符:== or === ?

    1. 四值逻辑的逻辑运算 在对比SystemVerilog中的相等运算符之前,先来看一下三种最基本的逻辑运算符,下文中以·表示与运算,以+表示或运算,以'表示非运算.我们都知道在逻辑代数中,只有0和1 ...

  6. Vue3全家桶升级指南二ref、toRef、toRefs的区别

    ref是对原始数据的拷贝,当修改ref数据时,模板中的视图会发生改变,但是原始数据并不会改变. toRef是对原始数据的引用,修改toRef数据时,原始数据也会发生改变,但是视图并不会更新. 在vue ...

  7. iOS工程师如何恍然大悟?

    聊聊行情?为什么总有人在乎旁人的看法而忽略自己的初衷? 虽然iOS开发市场说不上好但也绝不算坏,想没想过那些煽风点火说iOS不行的人在做什么? 真的转行从头开始? 错.大错特错! 在劝退你的同时他们会 ...

  8. hdu 6050 Funny Function 矩阵快速幂

    就算告诉我是矩阵快速幂我也推不出递推式呀!!! 官方题解: 对于任意i>=1,当j>=3时,有通过归纳法可以得到 进而推导出 后来自己重新推导了一遍 #include <iostre ...

  9. POJ 2506 Tiling dp+大数 水题

    大致题意:现有两种方块(1X2,2X2),方块数量无限制.问用这两种方块填满2Xn的矩阵的填法有多少种. 分析:通俗点说,找规律.专业化一点,动态规划. 状态d[i],表示宽度为i的填法个数. 状态转 ...

  10. QQ邮箱获取授权码方法

    1.登录QQ邮箱,点击"设置" 2.点击"账户" 3.开启POP3/SMTP服务 4.点击"生成授权码" 5.完成验证后,即可生成授权码 P ...