luogu题解 P1099 【树网的核】树的直径变式+数据结构维护
题目链接:
分析:
首先你需要\(O(N)\)求树的直径的前置技能,其实很简单,先随便找个根找到树上距离它最远的顶点\(S\),然后以\(S\)为根找树上距离\(S\)最远的顶点\(E\),\(S,E\)之间的路径就是树的直径
暴力枚举
按照题目要求说的去做就好了,求出树的直径,然后根据贪心找长度小于等于s的路径搜一遍就好了,时间复杂度\(O(N^2)\) 代码难度小 可过NOIP数据
预处理扫描
还是先求出树的直径,然后通过仔细分析偏心距怎么求,发现无非三种情况
1.直径最左端顶点到路径最左端顶点距离
2.路径上各点不经过直径上其他点到达的最大距离
3.直径上最右端顶点到路径最右端顶点的距离
我相信2理解不难,现在简单证明1情况(3同理)的正确性:假设路径最左端有一条向左延伸的路径其终点\(E'\)不是直径左端点而距离却更大,则违反直径定义,因为这样\(E'\)与路径左端点连接能形成一条更长的直径
于是我们需要四遍DFS(也许你写的好的话并不需要这么多遍)
前两遍就是求树的直径,用一个\(dmet[]\)将直径各点按顺序记录方便区间处理,\(diameter\)为直径长度
第三遍求直径上各点不经过直径上其他点达到的最大距离,用\(d[]\)记录
第四遍求直径左右段点到直径各点距离,用\(ld[],rd[]\)记录
然后我们扫描长度小于等于s的路径(可能是一个点),找上述三种情况的最大值,找第二种情况最大值你可以用滑动窗口,ST Table,线段树在区间查找最大值,这里采用线段树
时间复杂度:\(O(N\log N)\)
可以过比较大的数据,这是加强版
代码
写得比较丑,见谅
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <queue>
#define ll long long
#define ri register int
using namespace std;
const int maxn=500005;
const int maxm=1000005;
const int inf=0x7fffffff;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;
return ;
}
struct Edge{
int ne,to,dis;
}edge[maxm];
int h[maxn],num_edge=0,mx=-inf;
bool vis[maxn];
void add(int f,int to,int dis){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
edge[num_edge].dis=dis;
h[f]=num_edge;
}
int n,s,head,tail;
void dfs_1(int fa,int cur,int cnt){
for(ri i=h[cur];i;i=edge[i].ne){
if(edge[i].to!=fa)dfs_1(cur,edge[i].to,cnt+edge[i].dis);
}
if(cnt>mx){
mx=cnt,head=cur;
}
return ;
}
int pre[maxn],dmet[maxn],d[maxn],diameter;
void dfs_2(int fa,int cur,int cnt){
for(ri i=h[cur];i;i=edge[i].ne){
if(edge[i].to!=fa){
pre[edge[i].to]=cur;
dfs_2(cur,edge[i].to,cnt+edge[i].dis);
}
}
if(cnt>mx){
mx=cnt,tail=cur;
}
return ;
}
int root;
void dfs_3(int fa,int cur,int cnt){
for(ri i=h[cur];i;i=edge[i].ne){
int v=edge[i].to;
if(!vis[v]&&v!=fa){
dfs_3(cur,v,cnt+edge[i].dis);
}
}
if(cnt>mx){
mx=cnt,d[root]=cnt;
}
return ;
}
int ld[maxn],rd[maxn],fun[maxn];
void dfs_4(int fa,int cur,int cnt){
for(ri i=h[cur];i;i=edge[i].ne){
int v=edge[i].to;
if(vis[v]&&v!=fa){
dfs_4(cur,v,cnt+edge[i].dis);
}
}
ld[fun[cur]]=cnt,rd[fun[cur]]=diameter-cnt;
return ;
}
int maxx[maxn<<2],L,R;
void build(int now,int l,int r){
if(l==r){
maxx[now]=d[dmet[l]];
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
maxx[now]=max(maxx[now<<1],maxx[now<<1|1]);
return ;
}
int query(int now,int l,int r){
if(L<=l&&r<=R){
return maxx[now];
}
int mid=(l+r)>>1,ans=-inf;
if(L<=mid)ans=max(ans,query(now<<1,l,mid));
if(mid<R)ans=max(ans,query(now<<1|1,mid+1,r));
return ans;
}
int main(){
int x,y,z;
read(n),read(s);
for(ri i=1;i<n;i++){
read(x),read(y),read(z);
add(x,y,z);
add(y,x,z);
}
mx=-inf;
dfs_1(0,1,0);
mx=-inf;
dfs_2(0,head,0);
diameter=mx; //两次DFS找直径
/*------------------*/
int tmp=tail,tot=0;
while(tmp!=head){
dmet[++tot]=tmp;
fun[tmp]=tot;
vis[tmp]=1;
tmp=pre[tmp];
}
dmet[++tot]=head;
fun[head]=tot;
vis[head]=1; //记录直径
/*------------------*/
for(ri i=1;i<=tot;i++){
root=dmet[i];
mx=-inf;
dfs_3(0,dmet[i],0);
} //找从直径各点不经过其他直径上的点走过的最大长度
dfs_4(0,tail,0);//找直径两端点到直径各点距离
build(1,1,tot);//建线段树,不会ST Table的我
/*-----------------*/
int ans=inf,l=1,r=1;
while(ld[r+1]-ld[l]<=s&&r!=tot)r++;
//cout<<tot<<'*'<<endl;
while(r<=tot){//cout<<l<<' '<<r<<endl;
L=l,R=r;
ans=min(ans,max(query(1,1,tot),max(ld[l],rd[r])));
if(r==tot)break;
l++;
while(ld[r+1]-ld[l]<=s&&r!=tot)r++;
}
printf("%d\n",ans);
return 0;
}
luogu题解 P1099 【树网的核】树的直径变式+数据结构维护的更多相关文章
- 【bzoj1999】[Noip2007]Core树网的核 树的直径+双指针法+单调队列
题目描述 给出一棵树,定义一个点到一条路径的距离为这个点到这条路径上所有点的距离的最小值.求一条长度不超过s的路径,使得所有点到这条路径的距离的最大值最小. 输入 包含n行: 第1行,两个正整数n和s ...
- luogu题解 P3629 【[APIO2010]巡逻】树的直径变式
题目链接: https://www.luogu.org/problemnew/show/P3629 分析 最近被众多dalao暴虐,这道题傻逼地调了两天才知道错哪 不过这题比较良心给你一个容易发现性质 ...
- 洛谷 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\) ...
- #P1099 树网的核 题解
题目描述 pdf 题解 这一题,刚开始看题目感觉好像很难,题目又长……一看数据范围,呵呵. 已经给出来这是个DAG,所以不用担心连通性的问题.那么怎么做呢? 朴素的做法是把树的直径的两个端点都统计出来 ...
- [NOIP2007] 提高组 洛谷P1099 树网的核
题目描述 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W表示各边长度的集合,并 ...
- 树网的核[树 floyd]
描述 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W表示各边长度的集合,并设T ...
随机推荐
- 忘记Linux 3.X/4.x/5.x 宝塔面板密码的解决方案
进入ssh 输入以下命令重置密码(把命令最后面的 “testpasswd” 替换成你要改的新密码)注:若是debian/ubuntu用户,请使用有root权限的账户去执行这条命令 cd /www ...
- Python是否存在方法方法重载及是否可以不显示声明初始化方法
一.python中是否存在方法重载 对java有了解的程序员都知道,java中存在构造方法重载和普通方法重载,重载指的是方法名相同,参数列表不同的多个方法.python中是否也支持这两种方法重载,测试 ...
- 前端性能之Chrome的Waterfall
浏览器根据HTML中外连资源出现的顺序,依次放入队列(队列),然后根据优先级确定向服务器获取资源的顺序.同优先级的资源根据HTML中出现的先后顺序来向服务器获取资源. 瀑布中各项内容的含义: 排队: ...
- 深入学习重点分析java基础---第一章:深入理解jvm(java虚拟机) 第一节 java内存模型及gc策略
身为一个java程序员如果只会使用而不知原理称其为初级java程序员,知晓原理而升中级.融会贯通则为高级 作为有一个有技术追求的人,应当利用业余时间及零碎时间了解原理 近期在看深入理解java虚拟机 ...
- 阶段3 3.SpringMVC·_06.异常处理及拦截器_5 SpringMVC拦截器之编写controller
先新建包,com.itcast.controller,然后把异常拦截的项目的UserController复制过来. 复制过来稍作修改 创建pages文件件,然后新建success.jsp页面 部署当前 ...
- mssql表分区
1:表分区 什么是表分区一般情况下,我们建立数据库表时,表数据都存放在一个文件里.但是如果是分区表的话,表数据就会按照你指定的规则分放到不同的文件里,把一个大的数据文件拆分为多个小文件,还可以把这些小 ...
- [笔记] Ubuntu 18.04安装Docker CE及NVIDIA Container Toolkit流程
之前写的[笔记] Ubuntu 18.04安装Docker CE及nvidia-docker2流程已经out了,以这篇为准. Docker的好处之一,就是在Container里面可以随意瞎搞,不用担心 ...
- golang(09) golang 接口内部实现
原文链接 http://www.limerence2017.com/2019/09/24/golang14/#more 前文介绍过golang interface用法,本文详细剖析interface内 ...
- shell脚本安装python、pip--这种写法是错误的---每一个命令执行完都要判断是否执行成功,否则无法进行下一步
shell脚本安装python.pip--不需要选择安装项目--不管用总报错,必须带上判断符号,while没有这种用法,写在这里为了以后少走弯路,所以不要用下面的执行了 首先把pip-.tgz 安装包 ...
- C# Path.Combine 缺陷(http路径用Uri类)
Path.Combine: 什么时候会用到Path.Combine呢?,当然是连接路径字符串的时候! 所以下面的代码可以完美的工作: public static void Main() { strin ...