[NOI2013]快餐店(基环树DP)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20180602_csq/article/details/102680231

[NOI2013]快餐店

题目描述

小T打算在城市C开设一家外送快餐店。送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近的地方。

快餐店的顾客分布在城市C的N 个建筑中,这N 个建筑通过恰好N 条双向道路连接起来,不存在任何两条道路连接了相同的两个建筑。任意两个建筑之间至少存在一条由双向道路连接而成的路径。小T的快餐店可以开设在任一建筑中,也可以开设在任意一条道路的某个位置上(该位置与道路两端的建筑的距离不一定是整数)。

现给定城市C的地图(道路分布及其长度),请找出最佳的快餐店选址,输出其与最远的顾客之间的距离。

输入格式

输入文件foodshop.in第一行包含一个整数N,表示城市C中的建筑和道路数目。

接下来N行,每行3个整数,Ai,Bi,Li(1≤i≤N;Li>0),表示一条道路连接了建筑Ai与Bi,其长度为Li 。

输出格式

输出文件foodshop.out仅包含一个实数,四舍五入保留恰好一位小数,表示最佳快餐店选址距离最远用户的距离。

注意:你的结果必须恰好有一位小数,小数位数不正确不得分。

输入输出样例

输入

4 
1 2 1 
1 4 2 
1 3 2 
2 4 1

输出

2.0 

输入

5
1 5 100
2 1 77
3 2 80
4 1 64
5 3 41

输出

109.0

说明/提示

样例1

样例2

数据范围

对于 10%的数据,N<=80,Li=1;

对于 30%的数据,N<=600,Li<=100;

对于 60% 的数据,N<=2000,Li<=10^9;

对于 100% 的数据,N<=10^5,Li<=10^9

题解

首先这个图是一棵基环树

思考一下最后的答案的位置到所有点的距离

画个图:

通过观察,我们可以发现环上总有一条边是不会经过的

我们可以枚举环上的断边,求剩下树的直径,答案就是直径/2

如果我们恰好枚举到了最优答案断的边,我们就可以得到最后的答案

于是我们就有了一个O(n)枚举环上断边+O(n)求树的直径的算法,总共是O(n^2)的

由于n<=100000,我们需要更优秀的算法

考虑用一个点作为起始点把基环展开,然后分别计算不跨过该点最长路径与跨过该点的最长路径(有点像分治)

得:

然后依次计算每棵子树的直径与最远的儿子

再求出所有点最远儿子到左端点的前缀最大值h1与所有点到右端点的后缀最大值h2

还要求出这些点前缀直径g1和后缀直径g2(有点像这道题

最后枚举一下断边就可以直接统计断一条边答案了,求这些答案的最小值再除以2就是最后答案

AC代码(感觉很难写,实际上不难,细节比较多):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
#define N 100005
int fir[N],to[2*N],nxt[2*N],cd[2*N],cnt;
void adde(int a,int b,int c)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cd[cnt]=c;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cd[cnt]=c;
}
#define LL long long
int cir[N],D[N],fa[N],dep[N];
bool vis[N];
void findcir(int u)
{
	dep[u]=dep[fa[u]]+1;
	int v,p;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v==fa[u]) continue;
		if(!dep[v]){fa[v]=u;findcir(v);}
		else if(dep[v]<dep[u]){
			for(int j=u;;j=fa[j]){
				vis[j]=1;cir[++m]=j;
				if(j==v)break;
			}
		}
	}
}
LL hei[N],ans;
void dfs(int u)
{
	int v,p,w;
	for(p=fir[u];p;p=nxt[p]){
		v=to[p];w=cd[p];
		if(v!=fa[u]&&!vis[v]){
			dfs(v);
			ans=max(hei[u]+hei[v]+w,ans);
			hei[u]=max(hei[u],hei[v]+w);
		}
	}
}
LL g1[N],g2[N],h1[N],h2[N];
int main()
{
	int n,i,u,v,w,p;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d%d%d",&u,&v,&w);
		adde(u,v,w);
	}
	findcir(1);
	for(i=1;i<=n;i++)
		if(vis[i]) dfs(i);
	cir[m+1]=cir[1];
	for(i=1;i<=m;i++)
		for(p=fir[cir[i]];p;p=nxt[p])
			if(to[p]==cir[i+1]){
				D[i]=cd[p];
				break;
			}
	LL sum=0,mx=hei[cir[1]]+D[1];
	for(i=2;i<=m;i++){
		sum+=D[i-1];
		h1[i]=max(h1[i-1],hei[cir[i]]+sum);
		g1[i]=max(g1[i-1],hei[cir[i]]+mx);
		mx=max(mx,hei[cir[i]])+D[i];
	}
	sum=0;mx=hei[cir[1]]+D[m];
	for(i=m;i>1;i--){
		sum+=D[i];
		h2[i]=max(h2[i+1],hei[cir[i]]+sum);
		g2[i]=max(g2[i+1],hei[cir[i]]+mx);
		mx=max(mx,hei[cir[i]])+D[i-1];
	}
	LL ret=1ll<<60;
	for(i=1;i<=m;i++)
		ret=min(ret,max(ans,max(h1[i]+h2[i+1],max(g1[i],g2[i+1]))));
	printf("%.1f",1.0*ret/2);
}

猜你喜欢

转载自blog.csdn.net/C20180602_csq/article/details/102680231