题解 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]== ...
随机推荐
- 加入购物车的功能wepy
1.先有一个加入购物车的按钮 <view wx:if="{{(detaildata.boughtNum < detaildata.buy_limit) && de ...
- tecplot-云图中显示等值线的值
原版视频下载地址:https://yunpan.cn/cx6IQkkYKIB99 访问密码 a3ee
- SignalR简单实用_转自:https://www.cnblogs.com/humble/p/3851205.html
一.指定通信方式 建立一个通讯方式需要一定的时间和客户机/服务器资源.如果客户机的功能是已知的,那么通信方式在客户端连接开始的时候就可以指定.下面的代码片段演示了使用AJAX长轮询方式来启动一个连接, ...
- 关于密码重用参数PASSWORD_REUSE_TIME,PASSWORD_REUSE_MAX之间的关系及其演示
转自: https://blog.51cto.com/carefree/1382811 测试环境:10.2.0.2.0测试用户:SCOTT测试用的三组密码:oracle1 oracle2 oracle ...
- RabbitMQ入门学习系列(二),单生产者消费者
友情提示 我对我的文章负责,发现好多网上的文章 没有实践,都发出来的,让人走很多弯路,如果你在我的文章中遇到无法实现,或者无法走通的问题.可以直接在公众号<爱码农爱生活 >留言.必定会再次 ...
- linux安装phantomjs,-bash: /usr/local/bin/phantomjs: is a directory解决方案
首先安装依赖——fontconfig和freetypeyum install fontconfig freetype2在官网上下载对应版本的包http://phantomjs.org/download ...
- 团队作业-Alpha(1/4)
队名:软工9组 组长博客: https://www.cnblogs.com/cmlei/ 作业博客: 组员进度 ● 组员一(组长) 陈明磊 ○过去两天完成了哪些任务 ●文字/口头描述 初步学习flas ...
- Mininet系列实验(七):Mininet脚本实现控制交换机行为
1 实验目的 熟悉Mininet自定义拓扑脚本的编写: 掌握使用“ovs-vsctl”命令直接控制Open vSwitch. 2 实验原理 在SDN环境中,控制器可以通过对交换机下发流表操作来控制交换 ...
- elementUI el-table合并单元格
合并单元格,如果id列值一致,则合并. <el-table :data="tableData6" :span-method="objectSpanMethod&qu ...
- Flutter ------- WebView加载网页
在Flutter 加载网页?也是有WebView的哦,和Android一样 1.添加依赖 dependencies: flutter_webview_plugin: ^0.2.1+2 2.导入库 im ...