[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). 这个国家的人对火焰有超越宇宙的热情,所以这个国家 ...
随机推荐
- ZOJ 3932 Handshakes
Last week, n students participated in the annual programming contest of Marjar University. Students ...
- timeline css
CODE <!doctype html> <html> <head> <meta charset="UTF-8"> <titl ...
- 使用 postMessage + iframe 实现跨域通信
一.postMessage window.postMessage() 方法可以安全地实现跨源通信.通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(44 ...
- jquery的常用知识点
一.用jquery寻找元素 1.选择器 基本选择器: $("*") $("#id") 用id匹配 $(".class") 用class名匹配 ...
- java基础02
使用 myeclipse写第一个java程序 之后 右键 点击 src 创建 Class /** * package:包的声明! * 代表我们当前的java源文件在项目中的位置! * 必须位于除了 ...
- 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活--hdu2191(多重背包模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2191 标准的多重背包 题目 有N种物品和一个容量为V的背包.第i种物品最多有n[i]件可用,每件费用是 ...
- 【我的Android进阶之旅】解决Center OS 64位系统编译Android APP报错error=2和finished with non-zero exit value 127
一.错误描述 1.问题 java.io.IOException: error=2, 没有那个文件或目录 今天在刚重新搭建好的64位的Center OS上安装好了Android SDK,Jenkins, ...
- Linux(5)- MariaDB、mysql主从复制、初识redis
一.MYSQL(mariadb) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可. 开发这个分支的原因之一是:甲骨文公司收购了MySQL后,有将MySQL ...
- 18.出现Description Resource Path Location Type Unknown error merging manifest
原因是,依赖工程和主工程的manifest中的 <uses-sdk android:minSdkVersion="9" android:targetSdkVersion=&q ...
- Git添加本地项目出现fatal: unable to get credential storage lock: File exists
把本地项目初始化之后上传到github上出现问题:fatal: unable to get credential storage lock: File exists 解决办法:是因为我上传用的git帐 ...