这题是真的水啊。。。

------------

昨天模拟赛考了这题,很多人都是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 【树网的核】的更多相关文章

  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. P2491 消防/P1099 树网的核

    P2491 消防/P1099 树网的核 双倍经验,双倍快乐. 题意 在一个树上选择一段总长度不超过\(s\)的链使所有点到该链距离的最大值最小. 输出这个最小的值. 做法 Define:以下\(s\) ...

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

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

  6. #P1099 树网的核 题解

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

  7. P1099 树网的核

    NOIP 2007 提高第四题. 啊......我还是看了题解才做出来的. 这题乍一看毫无头绪,但是我们spy on一下,暗中观察发现:n才300!随便打暴力水过去啊! 然后,这破题怎么暴力?感觉我的 ...

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

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

  9. 洛谷P1099 树网的核

    传送门 80分 $ Floyd $ 树的直径可以通过枚举求出.直径的两个端点$ maxi,maxj $ ,由此可知对于一个点 $ k $ ,如果满足 $ d[maxi][k]+d[k][maxj]== ...

随机推荐

  1. 反手来个K8S入门到跑路

    layout: post title: 反手来个K8S入门到跑路 category: linux date: 2019-06-09 tags: linux k8s 反手来个K8S入门到跑路 前言 放假 ...

  2. ubuntu之路——day8.4 Adam自适应矩估计算法

    基本上讲,Adam就是将day8.2提到的momentum动量梯度下降法和day8.3提到的RMSprop算法相结合的优化算法 首先初始化 SdW = 0 Sdb = 0 VdW = 0 Vdb = ...

  3. 手把手教你如何使用量产工具修复u盘

    u盘是我们平时都使用到的一个小工具,我们会使用它来进行储存一些重要的文件,但要是我们操作不当的话,那么就会导致u盘出现一些问题的哟,比如说插入u盘无法打开文件等等,那么遇到这个问题时该这么办呢?那么, ...

  4. linux中截取字段与#、$区别

    1.Linux shell 截取字符变量的前8位 实现方法有如下几种: expr substr “$a” 1 8 echo $a|awk ‘{print substr(,1,8)}’ echo $a| ...

  5. windows下jenkins的安装与配置

    项目自动化部署: https://www.cnblogs.com/wuxunyan/p/9592953.html https://blog.csdn.net/qq_37372007/article/d ...

  6. python socketpool:通用连接池(转)

    简介 在软件开发中经常要管理各种“连接”资源,通常我们会使用对应的连接池来管理,比如mysql数据库连接可以用sqlalchemy中的池来管理,thrift连接可以通过thriftpool管理,red ...

  7. 漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析

    漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析 漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析 - FreeBuf互联网安全新媒体平台 https: ...

  8. SQLServer stuff函数

    STUFF ( character_expression , start , length ,character_expression ) 参数 character_expression 一个字符数据 ...

  9. 008 BOM

    一:说明 1.说明 浏览器对象模型 2.顶级对象 浏览器中的顶级对象是window 页面中的顶级对象是document 因此: 变量属于window的,函数也是window的. 就可以使用window ...

  10. docker容器日志查看

    日志分两类,一类是 Docker 引擎日志:另一类是 容器日志. Docker 引擎日志 Docker 引擎日志 一般是交给了 Upstart(Ubuntu 14.04) 或者 systemd (Ce ...