[FROM WOJ]#4238 [Noi2013]快餐店

#4238 [Noi2013]快餐店

题面
小T打算在城市C开设一家外送快餐店。送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近的地方。
快餐店的顾客分布在城市C的N 个建筑中,这N 个建筑通过恰好N 条双向道路连接起来,不存在任何两条道路连接了相同的两个建筑。任意两个建筑之间至少存在一条由双向道路连接而成的路径。小T的快餐店可以开设在任一建筑中,也可以开设在任意一条道路的某个位置上(该位置与道路两端的建筑的距离不一定是整数)。
现给定城市C的地图(道路分布及其长度),请找出最佳的快餐店选址,输出其与最远的顾客之间的距离。

输入
第一行包含一个整数N,表示城市C中的建筑和道路数目。
接下来N行,每行3个整数,Ai,Bi,Li(1≤i≤N;Li>0),表示一条道路连接了建筑Ai与Bi,其长度为Li 。

输出
仅包含一个实数,四舍五入保留恰好一位小数,表示最佳快餐店选址距离最远用户的距离。
注意:你的结果必须恰好有一位小数,小数位数不正确不得分。

样例输入
5
1 5 100
2 1 77
3 2 80
4 1 64
5 3 41

样例输出
109.0

数据范围
对于 10%的数据, N &lt; = 80 , L i = 1 N&lt;=80,Li=1 N<=80,Li=1
对于 30%的数据, N &lt; = 600 , L i &lt; = 100 N&lt;=600,Li&lt;=100 N<=600Li<=100
对于 60% 的数据, N &lt; = 2000 , L i &lt; = 1 0 9 N&lt;=2000,Li&lt;=10^9 N<=2000Li<=109
对于 100% 的数据, N &lt; = 1 0 5 , L i &lt; = 1 0 9 N&lt;=10^5,Li&lt;=10^9 N<=105Li<=109

SOL
我们先不考虑基环树上的答案——如果是一颗树,那么显然答案一定为树的直径的一半。
如果这点想通了,其实很好往基环树上类推。
最长路径分2种情况——经过环上边和不经过环上边的:
基环树的最长路径不经过环上边:
选择你喜欢的方法搜出环,然后对环上的每一个点dp一次就可以了
基环树的最长路径经过环上边:
维护环上前后缀链直径最大即可。
维护以下三个值的最小值:
前缀链长度+当前节点子树最大深度+后缀链长度+当前节点子树最大深度
前缀中两个点子树的最大深度+两点之间的距离
后缀中两个点子树的最大深度+两点之间的距离
每次再取最大值

取上述2种情况的max再除2即为答案。

代码:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
namespace Base{
	template<typename T>inline void chmax(T &a,T b){return a>b?a:b;}
	template<typename T>inline void chmin(T &a,T b){return a<b?a:b;}
}using namespace Base;
inline int rd(){
	int re data=0;static char ch=0;ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data;
}
inline void write(int x){if(x>9)write(x/10);putchar(x%10+'0');}
const int N=1e5+5;
int n,cnt,ans1,ans2,first[N],top,stk[N],dis[N],val[N],len[N],fa[N],tcnt,dfn[N];bool mark[N];
struct edge{int v,w,nxt;}e[N<<1];
struct node{int x,y;}fr[N],bk[N];
inline void add(const int&u,const int&v,const int&w){e[++cnt]=(edge){v,w,first[u]};first[u]=cnt;}
inline void dfs(int u){
	dfn[u]=++tcnt;
	for(int re i=first[u];i;i=e[i].nxt){
		int re v=e[i].v;
		if(v==fa[u])continue;
		if(!dfn[v])val[v]=e[i].w,fa[v]=u,dfs(v);
		else if(dfn[v]>dfn[u]){
			for(;v!=u;v=fa[v])stk[++top]=v,mark[v]=1,len[top]=val[v];
			stk[++top]=u,mark[u]=1,len[top]=e[i].w;
		}		
	}
}
inline void dp(int u,int fat){
	for(int re i=first[u];i;i=e[i].nxt){
		int re v=e[i].v;
		if(v==fat||mark[v])continue;
		dp(v,u),ans1=max(ans1,dis[u]+dis[v]+e[i].w),dis[u]=max(dis[u],dis[v]+e[i].w);
	}
}
inline void solve(){
	dfs(1);
	for(int re i=1;i<=top;i++)dp(stk[i],0);
	int re tot=0,maxl=0,tmp=len[top];
	for(int re i=1;i<=top;i++){
		tot+=len[i-1],fr[i].x=max(fr[i-1].x,dis[stk[i]]+tot);
		fr[i].y=max(fr[i-1].y,maxl+dis[stk[i]]+tot),maxl=max(maxl,dis[stk[i]]-tot);
	}
	maxl=tot=0,len[top]=0;
	for(int re i=top;i>=1;i--){
		tot+=len[i],bk[i].x=max(bk[i+1].x,dis[stk[i]]+tot);
		bk[i].y=max(bk[i+1].y,maxl+dis[stk[i]]+tot),maxl=max(maxl,dis[stk[i]]-tot);
	}ans2=fr[top].y;
	for(int re i=1;i<top;i++)ans2=min(ans2,max(max(fr[i].y,bk[i+1].y),fr[i].x+bk[i+1].x+tmp));
	printf("%.1lf",(double)max(ans1,ans2)*1.00/2.00),exit(0);
}
signed main(){
	n=rd();
	for(int re i=1;i<=n;i++){int re u=rd(),v=rd(),w=rd();add(u,v,w),add(v,u,w);}
	solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值