[SDOI2011]消防/[NOIP2007] 树网的核
消防
题目描述
某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000)。
这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。
现在这个国家的经费足以在一条边长度和不超过s的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。
你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。
输入输出格式
输入格式:
输入包含n行:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为城市的个数,s为路径长度的上界。设结点编号以此为1,2,……,n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
输出格式:
输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。
输入输出样例
输入样例#1:
5 2
1 2 5
2 3 2
2 4 4
2 5 3
输出样例#1:
5
输入样例#2:
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
输出样例#2:
5
说明
【数据规模和约定】
对于20%的数据,n<=300。
对于50%的数据,n<=3000。
对于100%的数据,n<=300000,边长小等于1000。
题解
这道题可以说是NOIP2007树网的核的数据加强版
那道题由于数据范围极小,所以\(O(n^3),O(n^2),O(nlogsum),O(n)\)都可以过
而这道题就只能后面两种做法了
但是无论如何,首先这道题需要会求树的直径,如果不会的在这里再贴一下代码+一点点注释
- 两遍dfs/bfs
Code
void dfs(int u,int fa) {
for(int i=head[u]i;i=e[i].next)
{
int to=e[i].to;
if(to!=fa) dis[to]=dis[u]+e[i],v,dfs(to,u)
}
}
int main()
{
//建边
int s=0,t=0;
dfs(1,0);
for(int i=1;i<=n;i++) if(!s || dis[i]>dis[s]) s=i;
memset(dis,0,sizeof(dis)); dfs(s,0);
for(int i=1;i<=n;i++) if(!t || dis[i]>dis[t]) t=i;
//原理:树的直径是树上最长的一条链,因此任取树上一个点,离它最远的点必然是树的直径上的一个端点,找到这个端点后再以它为根,找到离它最远的点,则必然是直径的另一个端点
}
- 树形dp
定义dp[i]为以i为根的子树的直径
Code
void DP(int u,int fa) {
for(int i=head[u];i;i=e[i].next) {
int to=e[i].to;
DP(to,u);
ans=max(ans,dp[u]+dp[to]+e[i].v);//此时dp[u]并不包括to这个儿子,
dp[u]=max(dp[u],dp[to]+e[i].v);
}//想一下吧,挺简单的
}
\(O(n^3)\)只要会求树的直径就直接枚举即可
\(O(n^2)\)就是枚举+贪心,在s的范围内,显然越远越好,那么我们确定一段,就可以确定另一段,这样就变成了\(O(n^2)\)
那么再这里就讲一下\(O(nlogsum)\)和\(O(n)\)的做法
\(O(nlogsum)\)
sum为树的总权值
我们依然定义偏心距为其他点到所选路径的最大值
容易发现,此题答案具有单调性,可以二分答案,那么问题就为
求一条路径,使得它的偏心距不超过二分值mid
怎么check?
设直径的两个端点u,v.在直径上找到与u的距离不超过mid的前提下,距离u最远的节点,作为p.同样找到一个距离v不超过mid的最远的节点q
根据直径的最长性,任何其他在u,p之间分叉的子树上的节点与p的距离都不会比与u的距离更远,因此,p,q就是尽量靠近树的中心的节点
我们需要判断p,q之间的距离是否超过s,除此之外还要判断它们之间的距离以及离这条路径最远的点的距离是否超过mid,如果都满足,这就是一个合法解,继续二分
\(O(n)\)
我们可以用一个单调队列来维护答案
依然先求出直径的两个端点
设直径上的点为\(u_1,u_2...u_t\),先把这几个节点标记为已访问,然后通过dfs,求出d[\(u_i\)],表示从\(u_i\)出发,不经过直径上的节点能够到达的最远距离
那么以\(u_i,u_j\)为端点的这条路径的偏心距就是:
\]
这个时候用单调队列维护就可以做到\(O(n)\)的了
#include<bits/stdc++.h>
#define Max(a,b) (a)>(b)?(a):(b)
#define Min(a,b) (a)<(b)?(a):(b)
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
inline int read(int &ans) {
ans=0;int f=1;char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1;i=getchar();}
while(i>='0' && i<='9') {ans=(ans<<1)+(ans<<3)+i-'0';i=getchar();}
return ans*f;
}
const int N=500010;
struct edge {
int to,v,next;
}e[2*N];
int n,m,L,cnt,ml,mr,s,ans=2147483647,he,ta;
int head[N],vis[N],dis[N],fa[N];
int q[N],pos[N],f[N];
inline void add(int a,int b,int c) {
e[++cnt].to=b;
e[cnt].v=c;
e[cnt].next=head[a];
head[a]=cnt;
}
void dfs(int u) {
for(int i=head[u];i;i=e[i].next) {
int to=e[i].to;
if(to!=fa[u]) fa[to]=u,dis[to]=dis[u]+e[i].v,dfs(to);
}
}
void find(int u) {
f[u]=dis[u];
for(int i=head[u];i;i=e[i].next)
if(!vis[e[i].to] && e[i].to!=fa[u])
find(e[i].to),f[u]=Max(f[u],f[e[i].to]);
}
int main()
{
int u,v,z;read(n);read(s);
For(i,1,n-1) read(u),read(v),read(z),add(u,v,z),add(v,u,z);
dfs(1);For(i,1,n) if(!mr||dis[i]>dis[mr]) mr=i;
memset(dis,0,sizeof(dis)); fa[mr]=0,dfs(mr);
For(i,1,n) if(!ml||dis[i]>dis[ml]) ml=i;
int l=ml,r=ml; while(l) vis[l]=1,l=fa[l]; l=ml;
while(l) {
while(dis[r]-dis[l]>s) r=fa[r];
find(l); f[l]-=dis[l];
while(he<=ta && pos[he]-dis[l]>s) he++;
while(he<=ta && f[l]>q[ta]) ta--;
q[++ta]=f[l],pos[ta]=dis[l];
ans=Min(ans,Max(q[he],Max(dis[l]-dis[mr],dis[ml]-dis[r])));
l=fa[l];
}
printf("%d\n",ans);
}
博主蒟蒻,随意转载.但必须附上原文链接
http://www.cnblogs.com/real-l/
[SDOI2011]消防/[NOIP2007] 树网的核的更多相关文章
- BZOJ2282 SDOI2011消防/NOIP2007树网的核(二分答案+树形dp)
要求最大值最小容易想到二分答案.首先对每个点求出子树中与其最远的距离是多少,二分答案后就可以标记上一些必须在所选择路径中的点,并且这些点是不应存在祖先关系的.那么如果剩下的点数量>=3,显然该答 ...
- luogu 2491 [SDOI2011]消防 / 1099 树网的核 单调队列 + 树上问题
Code: #include<bits/stdc++.h> #define ll long long #define maxn 300001 #define inf 1000000000 ...
- NOIP2007 树网的核 && [BZOJ2282][Sdoi2011]消防
NOIP2007 树网的核 树的直径的最长性是一个很有用的概念,可能对一些题都帮助. 树的直径给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和.树中最远的两个节点之间 ...
- Cogs 97. [NOIP2007] 树网的核 Floyd
题目: http://cojs.tk/cogs/problem/problem.php?pid=97 97. [NOIP2007] 树网的核 ★☆ 输入文件:core.in 输出文件:core ...
- P2491 消防/P1099 树网的核
P2491 消防/P1099 树网的核 双倍经验,双倍快乐. 题意 在一个树上选择一段总长度不超过\(s\)的链使所有点到该链距离的最大值最小. 输出这个最小的值. 做法 Define:以下\(s\) ...
- noip2007 树网的核
P1099 树网的核 112通过 221提交 题目提供者该用户不存在 标签动态规划树形结构2007NOIp提高组 难度提高+/省选- 提交该题 讨论 题解 记录 题目描述 设T=(V, E, W) ...
- 洛谷1099 [NOIP2007] 树网的核
链接https://www.luogu.org/problemnew/show/P1099 题目描述 设T=(V,E,W)是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称TTT为 ...
- NOIP2007 树网的核 [提高组]
题目:树网的核 网址:https://www.luogu.com.cn/problem/P1099 题目描述 设 T=(V,E,W)T=(V,E,W) 是一个无圈且连通的无向图(也称为无根树),每条边 ...
- noip2007树网的核
想一下可以发现随便枚举一条直径做就可以了. 核越长越好.于是枚举核的过程可以做到O(n) 然后就是统计答案. 对于每个核最大偏心距肯定是核上面每个点不走核内的点所能走到的最远点的最值. 而且对于核的两 ...
随机推荐
- 网站标题被篡改成北京赛车、PK10的解决处理办法
客户网站于近日被跳转到赌博网站,打开后直接跳转到什么北京赛车,PK10等内容的网站上去,客户网站本身做了百度的推广,导致所有访问用户都跳转到赌博网站上去,给客户带来很大的经济损失,再一个官方网站的形象 ...
- ES6--Set之再理解
Set 其实2016年就看过阮大神的ECMAScript 6 入门,当时看了Set之后,大致看懂了,但事实上根本没有理解Set到底是什么,所以更记不住,平时做项目大多用到的还是ES5的传统写法,以至于 ...
- python2.7入门---正则表达式
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配.Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式.re 模块使 Pytho ...
- struts2官方 中文教程 系列七:消息资源文件
介绍 在本教程中,我们将探索使用Struts 2消息资源功能(也称为 resource bundles 资源绑定).消息资源提供了一种简单的方法,可以将文本放在一个视图页面中,通过应用程序,创建表单字 ...
- 第二十三篇 logging模块(******)
日志非常重要,而且非常常用,可以通过logging模块实现. 热身运动 import logging logging.debug("debug message") logging. ...
- LeetCode 410——分割数组的最大值
1. 题目 2. 解答 此题目为 今日头条 2018 AI Camp 5 月 26 日在线笔试编程题第二道--最小分割分数. class Solution { public: // 若分割数组的最大值 ...
- SpringBoot:工厂模式实现定时任务可配置
pringBoot:工厂模式实现定时任务可配置 需要:使用springboot,实现定时任务可配置. 定时任务可在代码中写死,在配置文件中配置,这些都不能实现定时任务在服务器不重启的情况下可配置. 为 ...
- 无缘无故出现npm 解析异常的的问题 解决方案
npm cache clean --force try if false delete package.lock.json try again if false npm set registry ht ...
- java编程思想 内容总结
Java编程思想重点笔记(Java开发必看) Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面 试过程中,而且 ...
- BZOJ 4004 JLOI2015 装备购买 高斯消元+线性基
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4004 Description 脸哥最近在玩一款神奇的游戏,这个游戏里有 n 件装备,每件装 ...