[SDOI2011]消防

题目

题目描述
某个国家有 nn 个城市,这 nn 个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为 z_iz
i

这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。

现在这个国家的经费足以在一条边长度和不超过s的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。

你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

输入格式
输入包含 nn 行:

第 11 行,两个正整数 nn 和 ss,中间用一个空格隔开。其中 nn 为城市的个数,ss 为路径长度的上界。设结点编号以此为1,2,\ldots,n1,2,…,n。

从第 22 行到第 nn 行,每行给出 33 个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,2 4 7表示连接结点 22 与 44 的边的长度为 77。

输出格式
输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。

输入输出样例
输入 #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%20% 的数据,n \le 300n≤300。

对于 50%50% 的数据,n \le 3 \times 10^3n≤3×10
3

对于 100%100% 的数据,1\le n \le 3 \times 10^51≤n≤3×10
5
,1\le z_i \le 10^31≤z
i

≤10
3

思路

我们可以想到,需要选的路径的所有边都在直径上是最优的。

因为离树上任意一点最远的点一定是直径的端点,所以我们选择的路径要与直径相交上才会产生最优解,

至于为什么整条路径都在直径上最优,

我们不妨假设现在有一棵树,

将直径上的一个点作为我们要选的路径上的一点,

我们要以这个点为中心扩展出一条路径,

离这个点最远的点一定是直径的端点,当前的最大值就是这个长度。

这时如果我们选的一条边不在直径上,并不会更新这个最大值,

这样即使不能继续在直径上延长,也对答案没有贡献,

于是为了方便处理,我们将整条路径都放在直径上,

但是这些不在直径上的路径的长度不能忽略,需要进行处理,

想通了这些,这道题就很简单了。

首先我们先用两遍bfs求出直径的两个端点,再把整条直径找出来,时间复杂度O(N)

然后对于每个直径上的点我们做一次dfs,求出不经过直径的以这个点源的路径的最大长度,一起取一个最大值,作为答案的最小值,这个步骤看上去是O(N^2),而实际上我们对于整棵树上的每一个点只会跑一次,于是时间复杂度为O(N)。

最后我们再以之前求得的最大值为左端点,直径的长度为右端点,二分答案,就可以很轻松的得出结果了。

代码

#include<bits/stdc++.h>
using namespace std;
struct Edge
{
	int to;
	int nxt;
	int len;
}e[600005];
int n,m,edgenum,head[300005],pa[300005],dep[300005],maxd[300005],f[300005],g[300005];
int up[300005],max1[300005],max2[300005],ans,p[300005],maxx;
void add(int u,int v,int l)
{
	e[++edgenum].len=l;
	e[edgenum].to=v;
	e[edgenum].nxt=head[u];
	head[u]=edgenum;
}
int ind;
void dfs1(int node)
{
	max1[node]=max2[node]=0;
	p[++ind]=node;
	for(int hd=head[node];hd;hd=e[hd].nxt)
	{
		int to=e[hd].to;
		if(to==pa[node])continue;
		dep[to]=dep[node]+e[hd].len;
		pa[to]=node;
		dfs1(to);
		if(max1[to]+e[hd].len>max1[node])
		{
			max2[node]=max1[node];
			max1[node]=max1[to]+e[hd].len;
		}
		else if(max1[to]+e[hd].len>max2[node])
			max2[node]=max1[to]+e[hd].len;
	}
	maxx=max(maxx,max1[node]+max2[node]);
}
void dfs2(int node)
{
	for(int hd=head[node];hd;hd=e[hd].nxt)
	{
		int to=e[hd].to;
		if(to==pa[node])continue;
		up[to]=up[node]+e[hd].len;
		if(max1[to]+e[hd].len==max1[node])
		  up[to]=max(up[to],max2[node]+e[hd].len);
		else up[to]=max(up[to],max1[node]+e[hd].len);
		dfs2(to);
	}
}
bool check(int x)
{
	ans=1000000000;
	for(int i=n;i>=1;i--)
	{
		int node=p[i];
		maxd[node]=dep[node];
		f[node]=g[node]=0;
		for(int hd=head[node];hd;hd=e[hd].nxt)
		{
			int to=e[hd].to;
			if(to==pa[node])continue;
			if(maxd[node]-dep[node]<=x&&maxd[to]-dep[node]<=x)f[node]=0;
			else if(maxd[node]-dep[node]<=x)f[node]=f[to]+e[hd].len;
			else if(maxd[to]-dep[node]<=x);
			else
			{
				if(g[node]==0)g[node]=f[node]+f[to]+e[hd].len;
				else g[node]=1000000000;
				f[node]=1000000000;
			}
			maxd[node]=max(maxd[node],maxd[to]);
		}
		if(maxd[node]-dep[node]<=x)f[node]=0;
		if(g[node]==0)g[node]=f[node];
		if(up[node]<=x)ans=min(ans,g[node]);
	}
	return ans<=m;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		int u,v,l;
		scanf("%d%d%d",&u,&v,&l);
		add(u,v,l);
		add(v,u,l);
	}
	dfs1(1);
	dfs2(1);
	int l=0,r=maxx;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))r=mid-1;
		else l=mid+1;
	}
	printf("%d\n",l);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值