这题是真的水啊。。。

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

昨天模拟赛考了这题,很多人都是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. mint-ui 做省市选择组件

    省市的数据是动态的,其实不是动态的更好搞 <!-- 省市选择 --> <mt-popup v-model="popupVisible" position=&quo ...

  2. 用vue做的购物车结算的功能

    <!-- 占位 --> <template> <div> <div class="product_table"> <div c ...

  3. hook cookie

    // ==UserScript== // @name cookie // @namespace http://tampermonkey.net/ // @version 0.1 // @descrip ...

  4. 一个想要拥有正常的F1~F12的联想小新潮

    联想如何切换至正常的F1~F12 Fn+Fx的生活让我疲倦,实在难受,就开始了尝试关闭Fn 问题一:无法打开BIOS设置界面 重启,在开机界面时,按F2???顺利开机-(相较于台式机开机时,可以按F2 ...

  5. JS 读取 获取 cookie

    alert(document.cookie); cookie 只能获取当前域名的cookie, 该页面的其他域名的获取不了的.

  6. 今天闲来无事给我这老伙计A4L换个机油

    今天闲来无事给我这老伙计A4L换个机油         今天天气不错正好心血来潮给我的老伙计做个小保健.跟我这么久了也不能亏待是吧,也很久没来论坛了顺便冒个泡给我们版主晶晶交个作业要不然又要揍我了. ...

  7. python简单图形界面GUI入门——easygui【转】

    原文:https://blog.csdn.net/mingqi1996/article/details/81272621 感觉gui做起来成就感比较高,学完基础语言顺便花一个下午看看GUI设计,现在回 ...

  8. mybatis pagehelper 分页 失效

    pagehelper 不分页几种情况的解决方法 - web洋仔 - CSDN博客https://blog.csdn.net/csdn___lyy/article/details/77160488 分页 ...

  9. ubuntu安装IntelliJ Idea及图标创建

    一.下载并解压安装 二.创建桌面程序 1. cd /usr/local/applications/ 2. vi idea.desktop 3. 内容如下 [Desktop Entry] Name=In ...

  10. 编写第一个dart程序hello dart

    /* 入口方法的两种定义方式 main(){ print('hello dart'); } */ ///这也是一个注释 //表示main方法没有返回值 void main(){ print('hell ...