[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). 这个国家的人对火焰有超越宇宙的热情,所以这个国家 ...
随机推荐
- 移动端click时间、touch事件、tap事件
一.click 和 tap 比较 两者都会在点击时触发,但是在手机WEB端,click会有 200~300 ms,所以请用tap代替click作为点击事件. singleTap和doubleTap 分 ...
- $obj->0
w对象 数组 分别对内存的 消耗 CI result() This method returns the query result as an array of objects, or an empt ...
- 三种方案在Windows系统下安装ubuntu双系统
一.虚拟机安装(不推荐) 使用工具:Vmware 如果不是因为迫不得已,比如Mac OS对硬件不兼容,Federa安装频繁出错,各种驱动不全等等,不推荐使用虚拟机安装. 个人感觉这是一种对操作系统的亵 ...
- python基础-第十篇-10.1HTML基础
htyper text markup language 即超文本标记语言 超文本:就是指页面内可以包含图片.链接,甚至音乐,程序等非文字元素 标记语言:标记(标签)构成的语言 网页==HTML文档,由 ...
- virtio后端驱动详解
2016-10-08 virtIO是一种半虚拟化驱动,广泛用于在XEN平台和KVM虚拟化平台,用于提高客户机IO的效率,事实证明,virtIO极大的提高了VM IO 效率,配备virtIO前后端驱动的 ...
- 需求-shidebing
# 原始数据 list1 = [ {"c_id": "101", "e_code": "201"}, {"c_ ...
- java 多线程 day11 lock
import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; /** * Create ...
- mysql监控优化(一)连接数和缓存
一.mysql的连接数 MYSQL数据库安装完成后,默认最大连接数是100,一般流量稍微大一点的论坛或网站这个连接数是远远不够的,连接数少的话,在大并发下连接数会不够用,会有很多线程在等待其他连接释放 ...
- WebStorm keyboard shortcuts
ctrl + D 向下复制 下面是Webstorm的一些常用快捷键: shift + enter: 另起一行 ctrl + alt + L: 格式化代码 control + E: 光标跳到行尾 it ...
- PL/SQL 创建用户及权限操作
1.创建User create user user01 identified by user01, 2.赋予连接数据库的权限 grant connect to user01; 3.把user00的表E ...