题解 Luogu P1099 【树网的核】
这题是真的水啊。。。
------------
昨天模拟赛考了这题,很多人都是O($n^3$)水过,但我认为,要做就做的足够好(其实是我根本没想到O($n^3$)的做法),然后就开始想O(n)的解法。
首先看题目,前面一大堆看似是废话,其实还是有很大用处的。
问题描述中提到了树的中心,但后面却貌似没有用到,其实中心是给我们带来提示的。
既然是求最小偏心距,那必然是要在直径上找,不然偏心距并不能有过多的减少,所以第一步,定下在直径上找。
然后直径上找也要讲究方法,假如整条路径在中心的左边或右边,那么偏心距就会过大了(s太小了也没办法了)。所以,如果s足够大的话,应该将路径放在中间(指的是路径左端点到直径的左端点与路径右端点到直径右端点的距离差最小),所以先定下中心所在的边上的两个端点(如果s太小就用两端点中靠近中心的一个端点),然后哪一边的偏心距(部分的)大,就往哪边拓展,相同则同时拓展。
有两点需要注意注意:
一、如果有两条或更多直径,走到几条直径的分叉点时,就停止往这边拓展了,并且如果另一边的偏心距(部分的)小于这边,那么就不必拓展了,反正拓展了后偏心距还是这边的值。
二、只有一条直径时,要注意找所选路径上所有的偏心距,然后取大,再与直径两端点到路径两端点的距离取大(这里是指左端点与左端点,右端点与右端点的距离)
然后就愉快的AC了!(实测BZOJ同样能过)
代码如下:(30ms,没进最优解QAQ)
#include<cstdio>
inline int read(){
int r=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')r=(r<<1)+(r<<3)+c-'0',c=getchar();
return r*f;
}
struct E{
int v,dis,nxt;
}e[1000001];
int n,s_e,S,head[500005],dis[500005],s,t,zj,next[2][500005][2],zx[4],f[2][500005],ans=1e9;//f是部分的偏心距,next是从s(或t)出发的下一个点(直径上)
bool bj[2][500005],jg[500005];//jg就是指直径上的点,bj指的是是不是直径分叉口
inline int max(int a,int b){
return a>b?a:b;
}
inline int min(int a,int b){
return a<b?a:b;
}
inline void a_e(int u,int v,int dis){
e[++s_e]=(E){v,dis,head[u]};
head[u]=s_e;
}
void dfs(int p,int fa,int lj){
dis[p]=lj;
for(int i=head[p];i;i=e[i].nxt){
int v=e[i].v;
if(fa==v)continue;
dfs(v,p,lj+e[i].dis);
}
}
int dfs_st(int p,int fa,int sta,int end){
int b=0,d=0;
if(p==end){
jg[p]=1;
return 1;
}
for(int i=head[p];i;i=e[i].nxt){
int v=e[i].v;
if(fa==v)continue;
b=max(b,dfs_st(v,p,sta,end));
if(b==1&&!d)d=i;
if(f[sta][p]<=f[sta][v]+e[i].dis){
if(f[sta][p]==f[sta][v]+e[i].dis)bj[sta][p]=1;
else f[sta][p]=f[sta][v]+e[i].dis,bj[sta][p]=0;
}
}
if(b){
next[sta][p][0]=e[d].v;
next[sta][p][1]=e[d].dis;
jg[p]=1;
return 1;
}
return 0;
}
void find_zx(int p,int dis){
int v=next[0][p][0],val=next[0][p][1];
if(2*(val+dis)>=zj){
zx[0]=p,zx[1]=v;
if(zj-2*dis<=2*(val+dis)-zj)zx[2]=p;//靠近p
if(zj-2*dis>2*(val+dis)-zj)zx[2]=v;//靠近v
zx[3]=val;
return;
}
find_zx(v,dis+val);
}
int ansl,ansr,anss=-1e9;
void solve(int l,int r,int dis){
if(!l||!r)return;
if(ans>max(f[0][r],f[1][l]))ans=max(f[0][r],f[1][l]),ansl=l,ansr=r;//记录下目前最优左端点与右端点
if(bj[0][l]||bj[1][l]){//分叉就判断
if(next[0][r][1]+dis<=S&&f[0][r]>f[1][l])solve(l,next[0][r][0],dis+next[0][r][1]);
}
else if(bj[0][r]||bj[1][r]){
if(next[1][l][1]+dis<=S&&f[0][r]<f[1][l])solve(next[1][l][0],r,dis+next[1][l][1]);
}
else {
if(f[0][r]>f[1][l]&&next[0][r][1]+dis<=S)solve(l,next[0][r][0],dis+next[0][r][1]);
else if(f[0][r]<f[1][l]&&next[1][l][1]+dis<=S)solve(next[1][l][0],r,dis+next[1][l][1]);
else if(dis+next[0][r][1]+next[1][l][1]<=S)solve(next[1][l][0],next[0][r][0],dis+next[0][r][1]+next[1][l][1]);
}
}
int find_s(int p,int fa){
int s=0;
for(int i=head[p];i;i=e[i].nxt){
int v=e[i].v;
if(fa==v||jg[v])continue;//直径上的点不需要找,ans已经记录下最优的了
s=max(s,find_s(v,p)+e[i].dis);//每个子节点里选最大
}
return s;
}
void find_ans(int p){
anss=max(anss,find_s(p,next[1][p][0]));//路径上每个点的部分偏心距取大
if(p==ansr)return;//到达另一个端点就没什么好找的了
find_ans(next[0][p][0]);
}
int main(){
freopen("core.in","r",stdin);
freopen("core.out","w",stdout);
n=read(),S=read();
for(int i=1;i<n;i++){
int u=read(),v=read(),dis=read();
a_e(u,v,dis);
a_e(v,u,dis);
}
dfs(1,0,0);//找直径
for(int i=1;i<=n;i++)
if(dis[s]<dis[i])s=i;
dfs(s,0,0);//找直径
for(int i=1;i<=n;i++)
if(dis[t]<dis[i])t=i;
zj=dis[t];
dfs_st(s,0,0,t);//找每个点的偏心距
dfs_st(t,0,1,s);
find_zx(s,0);//找直径中心
if(zx[3]<=S){//能选中心所在的边的两个端点就选
solve(zx[0],zx[1],zx[3]);
find_ans(ansl);//以防止有一条直径的情况
ans=max(ans,anss);
}
else ans=max(f[0][zx[2]],f[1][zx[2]]);//不能就找最挨近中心的点的偏心距
printf("%d",ans);
return 0;
}
~~完结偷偷撒花~~✿✿ヽ(°▽°)ノ✿
题解 Luogu P1099 【树网的核】的更多相关文章
- 洛谷 P1099 树网的核
P1099 树网的核 题目描述 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W ...
- bzoj1999 / P1099 树网的核
P1099 树网的核 (bzoj数据加强) 前置知识:树的直径 (并不想贴我的智障写法虽然快1倍但内存占用极大甚至在bzoj上MLE) 正常写法之一:用常规方法找到树的直径,在直径上用尺取法找一遍,再 ...
- P1099 树网的核——模拟+树形结构
P1099 树网的核 无根树,在直径上找到一条长度不超过s的路径,使得最远的点距离这条路径的距离最短: 首先两遍dfs找到直径(第二次找的时候一定要吧father[]清零) 在找到的直径下枚举长度不超 ...
- P2491 消防/P1099 树网的核
P2491 消防/P1099 树网的核 双倍经验,双倍快乐. 题意 在一个树上选择一段总长度不超过\(s\)的链使所有点到该链距离的最大值最小. 输出这个最小的值. 做法 Define:以下\(s\) ...
- [NOIP2007] 提高组 洛谷P1099 树网的核
题目描述 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W表示各边长度的集合,并 ...
- #P1099 树网的核 题解
题目描述 pdf 题解 这一题,刚开始看题目感觉好像很难,题目又长……一看数据范围,呵呵. 已经给出来这是个DAG,所以不用担心连通性的问题.那么怎么做呢? 朴素的做法是把树的直径的两个端点都统计出来 ...
- P1099 树网的核
NOIP 2007 提高第四题. 啊......我还是看了题解才做出来的. 这题乍一看毫无头绪,但是我们spy on一下,暗中观察发现:n才300!随便打暴力水过去啊! 然后,这破题怎么暴力?感觉我的 ...
- P1099 树网的核 && P2491 [SDOI2011]消防
给定一棵树, 你可以在树的直径上确定一条长度不超过 \(S\) 的链, 使得树上离此链最长的点距离最小, 输出这个距离 P2491 数据范围为 P1099 的 \(1000\) 倍 Solution ...
- 洛谷P1099 树网的核
传送门 80分 $ Floyd $ 树的直径可以通过枚举求出.直径的两个端点$ maxi,maxj $ ,由此可知对于一个点 $ k $ ,如果满足 $ d[maxi][k]+d[k][maxj]== ...
随机推荐
- 第10组 Alpha冲刺(2/6)
链接部分 队名:女生都队 组长博客: 博客链接 作业博客:博客链接 小组内容 恩泽(组长) 过去两天完成了哪些任务 描述 了解了如何根据系统获取的实际情况进行后端任务的调整 网易云音乐推荐算法的分析 ...
- Gated CNN 阅读笔记
之前看过TCN,稍微了解了一下语言模型,这篇论文也是对语言模型建模,但是由于对语言模型了解不深,一些常用数据处理方法,训练损失的计算包括残差都没有系统的看过,只是参考网上代码对论文做了粗浅的复现.开学 ...
- elementUI 列表里面含有多选框,当翻页的时候依然保持之前页多选不变
el-table的type="selection"的使用 场景:el-table,type="selection"时,重新请求后,设置列表更新前的已勾选项 踩坑 ...
- bat 获取 exe 文件中 产品版本号并存储到变量中
set EXE='D:\gitlab\drivereasy3\DriverEasyWPF\bin\Release\DriverEasy.exe' powershell "(Get-Item ...
- 教你玩转Linux—用户账号的管理
用户账号的管理工作主要涉及到用户账号的添加.修改和删除.添加用户账号就是在系统中创建一个新账号,然后为新账号分配用户号.用户组.主目录和登录Shell等资源.刚添加的账号是被锁定的,无法使用. 1.添 ...
- memcpy字节序问题
/* memcpy用法详解 */ #include <stdio.h> #include <stdlib.h> #include <string.h> //memc ...
- libfacedetection
libfacedetection测试 #include <stdio.h> #include <opencv2/opencv.hpp> #include <facedet ...
- MySQL数据库双机热备------主-主备份配置
MySQL数据库双机热备------主-主备份配置 实验环境: 主1数据库 192.168.1.1 centos6.5 x86_64 +MySQL5.5.35 主2数据库192.168.1.2 Wi ...
- Qt编写气体安全管理系统15-网络转发
一.前言 在本系统中网络转发是个什么功能含义呢,其实就是将本地采集设备的所有数据打包发送到指定的网络地址,默认采用UDP的形式,无连接开销小,我也是看到很多的组态软件有这个功能,其实现有的很多的气体探 ...
- Python - Django - ORM 聚合查询和分组查询
models.py: from django.db import models # 出版社 class Publisher(models.Model): id = models.AutoField(p ...