[BZOJ2282]消防
Description
Input
输入包含n行:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为城市的个数,s为路径长度的上界。设结点编号以此为1,2,……,n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
Output
输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。
Sample Input
5 2
1 2 5
2 3 2
2 4 4
2 5 3
【样例输入2】
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
Sample Output
5
【样例输出2】
5
HINT
对于100%的数据,n<=300000,边长小等于1000。
Source
首先我们应该想到这条路径一定在树的直径上。这里给出证明:
设直径的长度为d,那么直径外的边长度一定小于等于d/2(否则该路径会与直径的一部分构成一条比直径还长的路径)
若该路径不在直径上,则直径的最远点到该路径的距离至少为d/2
而如果在直径上,一定存在一段路径使得树上所有点的距离到它不超过d/2
所以选在直径上一定是优的,而且在符合题目要求的情况下越长越好。
那么如果我们确定里直径,我们可以O(n)求出直径上每个点到最远点的距离,然后左右指针扫直径,单调队列维护区间最小值即可。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
deque<int>q;
struct point
{
int to;
int next;
int dis;
}e[];
int n,x,y,z,res=,maxn,s,t,num,ss;
int d[],head[],f[],pre[];
bool vis[];
void add(int from,int to,int dis)
{
e[++num].next=head[from];
e[num].to=to;
e[num].dis=dis;
head[from]=num;
}
void dfs(int x)
{
vis[x]=true;
for(int i=head[x];i!=;i=e[i].next)
{
int to=e[i].to;
if(!vis[to])
{
d[to]=d[x]+e[i].dis;
dfs(to);
}
}
}
void dfs1(int x)
{
vis[x]=true;
for(int i=head[x];i!=;i=e[i].next)
{
int to=e[i].to;
if(!vis[to])
{
pre[to]=x;
d[to]=d[x]+e[i].dis;
dfs1(to);
}
}
}
void find(int x)
{
vis[x]=true;
for(int i=head[x];i!=;i=e[i].next)
{
int to=e[i].to;
if(!vis[to])
{
find(to);
d[x]=max(d[x],d[to]+e[i].dis);
}
}
}
void getf()
{
int slr=;
for(int i=t;i!=;i=pre[i])
{
f[i]=slr;
for(int j=head[i];j!=;j=e[j].next)
{
int to=e[j].to;
if(pre[i]==to)
{
slr+=e[j].dis;
break;
}
}
}
}
int main()
{
scanf("%d%d",&n,&ss);
for(int i=;i<=n-;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs();
for(int i=;i<=n;i++)
{
if(d[i]>maxn)
{
maxn=d[i];
s=i;
}
}
memset(vis,false,sizeof(vis));
memset(d,,sizeof(d));
dfs1(s);
maxn=;
for(int i=;i<=n;i++)
if(d[i]>maxn)
{
maxn=d[i];
t=i;
}
memset(vis,false,sizeof(vis));
memset(d,,sizeof(d));
for(int i=t;i!=;i=pre[i])
vis[i]=true;
for(int i=t;i!=;i=pre[i])
find(i);
getf();
int r=t,l=pre[t];
q.push_front(l);
if(d[l]<d[r])
q.push_back(r);
while()
{
if(pre[l]!=&&f[pre[l]]-f[r]<=ss)
{
l=pre[l];
int ans=max(f[r],maxn-f[l]);
q.push_front(l);
while(d[q.back()]<d[l])
q.pop_back();
ans=max(ans,d[q.back()]);
res=min(res,ans);
}
else if(l!=r)
{
if(q.back()==r)
q.pop_back();
r=pre[r];
int ans=max(f[r],maxn-f[l]);
ans=max(ans,d[q.back()]);
res=min(res,ans);
}
else if(l==r&&pre[l])
{
while(!q.empty())
q.pop_back();
l=pre[l];
r=l;
q.push_front(l);
int ans=max(f[r],maxn-f[l]);
ans=max(ans,d[q.back()]);
res=min(res,ans);
}
else
break;
}
printf("%d",res);
return ;
}
这道题综合了许多算法,也有一定的思维难度,是一道不可多得好题
[BZOJ2282]消防的更多相关文章
- [Bzoj2282]消防(二分答案+树的直径)
Description 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这个国家的人对火焰有超越宇宙的热情,所以这个国家 ...
- 【BZOJ2282】[Sdoi2011]消防 树形DP+双指针法+单调队列
[BZOJ2282][Sdoi2011]消防 Description 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这 ...
- BZOJ2282 SDOI2011消防/NOIP2007树网的核(二分答案+树形dp)
要求最大值最小容易想到二分答案.首先对每个点求出子树中与其最远的距离是多少,二分答案后就可以标记上一些必须在所选择路径中的点,并且这些点是不应存在祖先关系的.那么如果剩下的点数量>=3,显然该答 ...
- BZOJ1999或洛谷1099&BZOJ2282或洛谷2491 树网的核&[SDOI2011]消防
一道树的直径 树网的核 BZOJ原题链接 树网的核 洛谷原题链接 消防 BZOJ原题链接 消防 洛谷原题链接 一份代码四倍经验,爽 显然要先随便找一条直径,然后直接枚举核的两个端点,对每一次枚举的核遍 ...
- BZOJ2282: [Sdoi2011]消防
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2282 答案一定是在直径上的一段,然后答案一定不会小于不在直径上的点到直径的距离(要是可以的话那 ...
- 【bzoj2282】[Sdoi2011]消防
两次bfs可得直径,答案一定不会小于所有点到直径的距离最大值,只要把直径上的边权设为0,任选直径上一点bfs可得将最大值作为二分下界,二分直径左右端点的舍弃部分 #include<algorit ...
- NOIP2007 树网的核 && [BZOJ2282][Sdoi2011]消防
NOIP2007 树网的核 树的直径的最长性是一个很有用的概念,可能对一些题都帮助. 树的直径给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和.树中最远的两个节点之间 ...
- bzoj2282
到路径的距离就是到路径上的点最近的距离首先看到最大值最小不难想到二分答案下面的问题就是怎么判断,显然我们是不能穷举路径的我们要找出消防路径的性质仔细研究就会发现消防路径一定是树的直径的一段,这样必然最 ...
- bzoj 2282 [Sdoi2011]消防(树的直径,二分)
Description 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这个国家的人对火焰有超越宇宙的热情,所以这个国家 ...
随机推荐
- oracle导入sql文件
oracle导入sql文件: 1.进入到sql文件目录下,登录需要导入文件的用户 打开cmd,输入以下命令,进入oracle, sqlplus username/password username:需 ...
- kubernetes使用中遇到的坑
随着kubernetes的发展现在使用的范围越来越广,在使用过程中碰到问题是避免不了的,有些时候一些坑能提前避免是最好的,下面我做一个小记录,把我们生产环境中遇到的坑总结下,方便后面查询同时也方便各位 ...
- [iOS微博项目 - 4.0] - 自定义微博cell
github: https://github.com/hellovoidworld/HVWWeibo A.自定义微博cell基本结构 1.需求 创建自定义cell的雏形 cell包含:内容.工具条 内 ...
- Linux 搭建Git服务器
安装Git yum install -y git git --version 创建 Git 用户 sudo adduser git // 设置密码 passwd git 导入公钥 find / -na ...
- 并发编程 - 进程 - 1.互斥锁/2.模拟抢票/3.互斥锁与join区别
1.互斥锁: 互斥锁:Lock 原理就是把并发变成串行,一个一个运行,不错乱,但效率低 保证多个进程修改一块数据时,大家是一个一个修改,不错乱 mutex.acquire() mutex.releas ...
- D. Two Paths---cf14D(树的直径)
题目链接:http://codeforces.com/problemset/problem/14/D 题意:有n个city ; n-1条路:求断开一条路之后分成的两部分所构成的树的直径的积最大是多少: ...
- print文档
文档 def print(self, *args, sep=' ', end='\n', file=None): # known special case of print ""& ...
- sql server常用性能计数器
https://blog.csdn.net/kk185800961/article/details/52462913?utm_source=blogxgwz5 https://blog.csdn.ne ...
- linux内核介绍
linux系统可以分为:包括用户空间和内核空间两个部分. 现代cpu通常实现了不同的工作模式,以ARM为例,实现了7种工作模式: 用户模式.快速中断.外部中断.管理模式.数据访问中止.系统模式.未定义 ...
- SDUT2857:艺术联合会(简单dp)
链接: http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2857 题目解析: 这是去年校赛的题目, ...