BZOJ1999 树网的核[数据加强版]
1999: [Noip2007]Core树网的核
Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 1214 Solved: 336
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1 2 5
2 3 2
2 4 4
2 5 3
Sample Output
HINT
对于70%的数据,n<=200000
对于100%的数据:n<=500000, s<2^31, 所有权值<500
==============================================
似乎SPOJ上加强版的数据...
Source
题意简述:
树上的任两点间都有唯一路径。定义某一点到树上某一路径的距离为该点到路径上所有点的路径长度中的最小值。
定义树中某条路径的“偏心距”为所有其它点到此路径的距离的最大值。定义树的直径为树的最长路径(可能不唯一)。给出一个有N个节点的无根树,请找出某个直径上的一段长度不超过S的路径(可能退化为一个点),使它的偏心距最小。请输出这个最小偏心距的值。
题目已经告诉你如下定理:树的所有直径的中点必然重合(这个中点可能在某条边上)。
算法简述:
这道题描述很复杂,考场上相对于NOIP级别有一定难度,主要是让人很蒙,很难有模板可以套用,所以唯一的方法就是按照题目中说的硬搞。
题目中给我们的条件只有几个定义。我们注意到关于“偏心距”的定义:是树的所有点中距“核”的距离最长的一个。而关于“核”的定义为:树的直径上的某一段。抓住这两个定义,我们就有了解题的方向。
第一步自然是找直径。我们实在是很想用Floyd来求直径,但N=300的范围在那里摆着,迫使我们要寻求更快的方法。我们注意到直径的一个性质:
对于直径中的任意一点,其距离树中其他点的最远距离不超过该点到达直径端点的距离。这是个显而易见的性质。由这个性质,我们可以导出如下引理:
对于树中的任意一点,距离其最远的点一定是树的直径的某一端点。
用同样的方法,我们很容易找到另一个端点,也就求出了直径。现在的问题是:直径可能有很多条,究竟取哪一条呢?其实任意一条都是可行的。我们做如下的说明:
首先,题目中告诉我们树的所有直径的中点必然重合,也就是告诉我们:所有的直径都是相交的。从而对于任意两条不同的直径,我们可以找到一个分叉点,我们记分叉部分的长度为L1,直径总长减去L1的长度记为L2。假设这个“核”没有经过分叉点,那么两种情况:
1、 在公共部分,其最小“偏心距”一定不大于L2;
2、 在分叉部分,其最小“偏心距”一定不小于L2。
假设这个“核”经过分叉点,那么其最小偏心距至少是L1。所以很明显,其最小“偏心距”所对应的“核”必然有一部分出现在所有直径的公共部分,而对于不完全在公共部分的“核”,其“偏心距”拥有同样的下界L1,也不会影响到最终结果。
第二步就是枚举所有可能的“核”。对于任意一个“核”,我们只要记录它在直径中的左右端点就能确定这个“核”。这似乎有O(N2)级别种“核”,但由于对“核”的长度限制,实际数量级别大概只达到O(N)。
最后一步就是计算树中所有点到“核”的距离,然后取最大值。很明显,这个距离就是该点与从该点起做DFS遍历所遍历到的第一个直径上的点之间的距离。 而我们所关心的只有那个最远距离,所以我们在实现的时候将其反过来做,我们寻找以“核”中的点为根的子树的最大深度(这个子树与直径不相交)。根据定义, 这所有子树的最大深度中的最大值就是该“核”的“偏心距”。这里有一个小优化,即对于任意一个“核”的端点,其最大子树深度就是该点到达相应的直径端点的 距离。
其实上面这么多,就两句话
1. 找出最长链
2. 随便用一个数据结构,维护链上的两个端点l,r,用子树中的最长链更新答案
AC代码:
(跑的有点慢)
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
inline const int read(){
register int x=,f=;
register char ch=getchar();
while(ch>''||ch<''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
const int N=5e5+;
int dis[N],f[N];
bool vis[N];
vector<pair<int,int> >b[N];
void dfs(int x){
for(int i=;i<b[x].size();i++){
int nx=b[x][i].first;
if(vis[nx]||nx==f[x]) continue;
f[nx]=x;
dis[nx]=dis[x]+b[x][i].second;
dfs(nx);
}
}
int main(){
int n=read(),s=read();
for(int i=;i<n;i++){
int u=read(),v=read(),w=read();
b[u].push_back(make_pair(v,w));
b[v].push_back(make_pair(u,w));
}
int l=,l2=;
dis[l]=;dfs(l);
memset(f,,sizeof f);
for(int i=;i<=n;i++) if(dis[i]>dis[l]) l=i;
dis[l]=;dfs(l);
for(int i=;i<=n;i++) if(dis[i]>dis[l2]) l2=i;
int ans=0x7fffffff,j=l2;
for(int i=l2;i;i=f[i]){
while(f[j]&&dis[i]-dis[f[j]]<=s) j=f[j];
ans=min(ans,max(dis[j],dis[l2]-dis[i]));
}
for(int i=l2;i;i=f[i]) vis[i]=;
for(int i=l2;i;i=f[i]) dis[i]=,dfs(i);
for(int i=;i<=n;i++) ans=max(ans,dis[i]);
printf("%d",ans);
return ;
}
树形dp找树的直径
void dfs(int u,int fa){
int mx = ;
for(int i=head[u];i;i=e[i].next){
int v = e[i].to;
int w = e[i].w;
if(v==fa) continue;
dfs(v,u);
ans = max(ans,dp[v]+mx+w);
mx = max(mx,dp[v]+w);
}
dp[u] = mx;
}
BZOJ1999 树网的核[数据加强版]的更多相关文章
- [BZOJ1999] 树网的核 [数据加强版] (树的直径)
传送门 如果只是想验证算法正确性这里是洛谷数据未加强版 Description 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称T为树网(treenet ...
- [bzoj1999]树网的核
从下午坑到网上..noip的数据太弱,若干的地方写挂结果还随便过= = 最坑的就是网上有些题解没考虑周全... 第一步是找直径,用两次bfs(或者dfs,Linux下系统栈挺大的..)解决.找出其中一 ...
- 洛谷P1099 BZOJ1999 树网的核 [搜索,树的直径]
洛谷传送门,BZOJ传送门 树网的核 Description 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称T为树网(treenetwork),其中V ...
- 5.19[bzoj树网的核]
围观了final,SJTU还是飞了,泽民同志劲啊! 膜拜归膜拜...回来开题 bzoj1999树网的核 最近就喜欢给自己找切不动的题...QAQ ok.....昨天在家里做了一个下午+晚上 又困&am ...
- [BZOJ1999][codevs1167][Noip2007]Core树网的核
[BZOJ1999][codevs1167][Noip2007]Core树网的核 试题描述 设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称T为树网(t ...
- BZOJ1999或洛谷1099&BZOJ2282或洛谷2491 树网的核&[SDOI2011]消防
一道树的直径 树网的核 BZOJ原题链接 树网的核 洛谷原题链接 消防 BZOJ原题链接 消防 洛谷原题链接 一份代码四倍经验,爽 显然要先随便找一条直径,然后直接枚举核的两个端点,对每一次枚举的核遍 ...
- bzoj1999 / P1099 树网的核
P1099 树网的核 (bzoj数据加强) 前置知识:树的直径 (并不想贴我的智障写法虽然快1倍但内存占用极大甚至在bzoj上MLE) 正常写法之一:用常规方法找到树的直径,在直径上用尺取法找一遍,再 ...
- 【bzoj1999】[Noip2007]Core树网的核 树的直径+双指针法+单调队列
题目描述 给出一棵树,定义一个点到一条路径的距离为这个点到这条路径上所有点的距离的最小值.求一条长度不超过s的路径,使得所有点到这条路径的距离的最大值最小. 输入 包含n行: 第1行,两个正整数n和s ...
- [bzoj1999][noip2007]Core树网的核
好久没写题解了.这题不算太水就写一下题解. 话说回来,虽然不水但是挺裸.可以说题意即一半题解了. 我猜粘了题面也没有人去看的,所以直接人话题意了. 给一棵树,点数1e6,(当年noip的n当然是只有3 ...
随机推荐
- vi的搜索和替换
搜索中进行替换 /which #搜索which cwthat #替换成that n #重复搜索 . #重复替换 一种类型的替换命令 g/pattern/s/old/new/g 第一个 g 表示是有选择 ...
- 【Flask】Sqlalchemy 子查询
### subquery:子查询可以让多个查询变成一个查询,只要查找一次数据库,性能相对来讲更加高效一点.不用写多个sql语句就可以实现一些复杂的查询.那么在sqlalchemy中,要实现一个子查询, ...
- B-树 C++模板类封装(有图有真相)
定义: 一棵m阶B-树是拥有以下性质的多路查找树: 1.非叶子结点的根结点至少拥有两棵子树: 2.每一个非根且非叶子的结点含有k-1个关键字以及k个子树,其中⌈m/2⌉≤k≤m: 3.每一个叶子结点都 ...
- OC_NSString、
8月17日NSString 一.字符串的创建 //1.创建常量字符串 NSString *str1 = @"hello world"; NSLog(@"%@", ...
- Eclipse SVN插件设置
项目开发中,开发人员用SVN来管理代码,在和服务器同步时,需要避免上传不必要的一些编译文件,如.class,.log,target等文件,这里需要设置同步选项. 打开Eclipse ---> W ...
- 在Linux系统下使用Docker以及Weave搭建Nginx反向代理
Hi, 今天我们将会学习如何使用 Weave 和 Docker 搭建 Nginx 的反向代理/负载均衡服务器.Weave 可以创建一个虚拟网络将 Docker 容器彼此连接在一起,支持跨主机部署及自动 ...
- 【codevs1907】方格取数3(最大流最小割定理)
网址:http://codevs.cn/problem/1907/ 题意:在一个矩阵里选不相邻的若干个数,使这些数的和最大. 我们可以把它看成一个最小割,答案就是矩阵中的所有数-最小割.先把矩阵按国际 ...
- Asp.net WebAPI 使用流下载文件注意事项
public HttpResponseMessage Post(string version, string environment, string filetype) { var path = @& ...
- phpPgAdmin (win)配置安装及远程访问
phpPgAdmin (win)配置安装 [1] 通过PostgreSQL的Application Stack Builder配置安装phpPgAdmin 1.确保PostgreSQL安装并正 ...
- 页面js框架
js 模板,所有页面控制js,以闭包形式编写代码,降低浏览器内存消耗,加快运行速度 请按照此结构编写,便于后期前端维护和修改1. [代码][JavaScript]代码 /** * js 模板,所有 ...