[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仅包含一个实数,四舍五入保留恰好一位小数,表示最佳快餐店选址距离最远用户的距离。

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

输入输出样例

输入样例#1:

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

输出样例#1:

2.0

输入样例#2:

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

输出样例#2:

109.0

数据范围

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

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

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

对于 100% 的数据,N<=105,Li<=109


基环树DP + 线段树

终于写完了

虽然代码很长但是这题的思路真是非常的清晰

所以并不是肥肠难写

题意就是求出基环树的直径的最小值/2

如果不是基环树答案就是树的直径/2

但是ta是一棵基环树

所以我们可以把这个环找出来

最后的答案基环树上的边一定最少有一条没有用

所以如果这个点在环上

我们就枚举删边,然后每次跑一遍树的直径取min

复杂度n^2

我们的瓶颈在于无法快速算出直径

所以我们可以把环单独处理出来放进一个数组里

然后像区间dp一样将这个数组倍长

求一个边权的前缀和Sum

然后再求出以环上的每个点为端点的不经过环中边的最长链dp

然后就可以每次O(1)的快速求出经过环上两点间的最长链了

\(dp[i] + dp[j] + Sum[j] - Sum[i]\)

然后就该枚举断边了

就是一只滑动一个长度为环的大小长度的区间

求出\(dp[i] + dp[j] + Sum[j] - Sum[i]\) 的最大值取min

这样我们就能快速求出树的直径了

需要用线段树维护dp[i]-Sum[i]和dp[j]+Sum[j]的max值

然后有可能这两个变量出现在同一位置

所以还要再维护个次大值

如果答案不在环上的话我们还要再对每个环上的每个点跑一次不经过环中的边的树的直径

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 200050 ;
const double INF = 1e18 ;
using namespace std ;
inline int read() {
	char c = getchar() ; int x = 0 , w = 1 ;
	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ;}
	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
	return x*w ;
}

int n ;
int hea[M] , num = 1 ;
int dfn[M] , low[M] , st[M] , cnt , top ;
int cnum , bel[M] ;
int impp[M] , pnum ;
int isp[M] , impnum ;
bool exist[M] , visp[M] ;
double Ans = INF , f[M] , g[M] ;
double dp[M] , Sum[M] , vf[M] , vc[M] ;
struct E {
	int Nxt , to ; double Dis ;
} edge[M << 1] ;
struct Node {
	int vfMpos , vcMpos ;
	int vfLpos , vcLpos ;
	double vfMax , vcMax ;
	double vfLax , vcLax ;
	inline Node () 
	{ vfMpos = vcMpos = vfLpos = vcLpos = vfMax = vcMax = vfLax = vcLax = 0 ; }
} ;
inline void add_edge(int from , int to , double Dis) {
	edge[++num].Nxt = hea[from] ; edge[num].to = to ;
	edge[num].Dis = Dis ; hea[from] = num ;
}

void tarjan(int u , int father) {
	dfn[u] = low[u] = ++cnt ; st[++top] = u ; exist[u] = true ;
	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
		int v = edge[i].to ;
		if(v == father) continue ;
		if(!dfn[v]) {
			tarjan(v , u) ;
			low[u] = min(low[u] , low[v]) ;
		}
		if(exist[v]) low[u] = min(low[u] , dfn[v]) ;
	}
	if(dfn[u] == low[u]) {
		++ cnum ; int sz = 0 ;
		while(st[top + 1] != u) {
			bel[st[top]] = cnum ;
			++ sz ;
			exist[st[top --]] = false ;
		}
		if(sz > 1)
		    for(int i = 1 ; i <= n ; i ++)
		        if(bel[i] == cnum)
		            impp[++pnum] = i , visp[i] = true ;
	}
}
double Dfs(int u , int father) {
	double temp = 0 ;
	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
		int v = edge[i].to ;
		if(v == father || visp[v]) continue ;
		temp = max(temp , edge[i].Dis + Dfs(v , u)) ;
	}
	return temp ;
}
double findR(int u , int father) {
	f[u] = g[u] = 0 ;
	for(int i = hea[u] ; i ; i = edge[i].Nxt) {
		int v = edge[i].to ;
		if(v == father || visp[v]) continue ;
		double temp = edge[i].Dis + findR(v , u) ;
		if(temp > f[u]) g[u] = f[u] , f[u] = temp ;
	    else if(temp > g[u]) g[u] = temp ;
	}
	Ans = max(Ans , f[u] + g[u]) ;
	return f[u] ;
}
struct Seg {
	# define ls (now << 1)
	# define rs (now << 1 | 1)
	Node t[M << 2] ;
	inline Node Merge(Node tl , Node tr) {
		Node t ;
		if(tl.vfMax > tr.vfMax) {
			t.vfMax = tl.vfMax ; t.vfMpos = tl.vfMpos ;
			if(tl.vfLax > tr.vfMax) t.vfLax = tl.vfLax , t.vfLpos = tl.vfLpos ;
			else t.vfLax = tr.vfMax , t.vfLpos = tr.vfMpos ;
		}
		else {
			t.vfMax = tr.vfMax ; t.vfMpos = tr.vfMpos ;
			if(tr.vfLax > tl.vfMax) t.vfLax = tr.vfLax , t.vfLpos = tr.vfLpos ;
			else t.vfLax = tl.vfMax , t.vfLpos = tl.vfMpos ;
		}
		if(tl.vcMax > tr.vcMax) {
			t.vcMax = tl.vcMax ; t.vcMpos = tl.vcMpos ;
			if(tl.vcLax > tr.vcMax) t.vcLax = tl.vcLax , t.vcLpos = tl.vcLpos ;
			else t.vcLax = tr.vcMax , t.vcLpos = tr.vcMpos ;
		}	
		else {
			t.vcMax = tr.vcMax ; t.vcMpos = tr.vcMpos ;
			if(tr.vcLax > tl.vcMax) t.vcLax = tr.vcLax , t.vcLpos = tr.vcLpos ;
			else t.vcLax = tl.vcMax , t.vcLpos = tl.vcMpos ;
		}
		return t ;
	}
	void Build(int l , int r , int now) {
		if(l == r) {
			t[now].vfMax = vf[l] ; t[now].vfMpos = l ; t[now].vfLax = - INF ; 
			t[now].vcMax = vc[l] ; t[now].vcMpos = l ; t[now].vcLax = - INF ;
			return ;
		}
		int mid = (l + r) >> 1 ;
		Build(l , mid , ls) ; Build(mid + 1 , r , rs) ;
		t[now] = Merge(t[ls] , t[rs]) ;
	}
	Node qry(int L , int R , int l , int r , int now) {
		if(l == L && r == R) return t[now] ;
		int mid = (l + r) >> 1 ;
		if(mid >= R) return qry(L , R , l , mid , ls) ;
		if(mid < L) return qry(L , R , mid + 1 , r , rs) ;
		else {
			Node tl = qry(L , mid , l , mid , ls) ;
			Node tr = qry(mid + 1 , R , mid + 1 , r , rs) ;
			return Merge(tl , tr) ; 
		}
	}
	# undef ls
	# undef rs
} T ;
inline double Solve() {
	Ans = INF ;
	Sum[1] = 0 ;
	int u = impp[1] ;
	for(int o = 1 ; o < pnum ; o ++)
		for(int i = hea[u] ; i ; i = edge[i].Nxt) {
			int v = edge[i].to ;
			if(v == impp[o + 1]) {
				Sum[o + 1] = Sum[o] + edge[i].Dis ;
				u = v ;
				break ;
			}
		}
	for(int i = 1 ; i <= pnum ; i ++) {
	    vf[i] = dp[impp[i]] - Sum[i] ;
	    vc[i] = dp[impp[i]] + Sum[i] ;
	}
	T.Build(1 , pnum , 1) ;
	for(int i = pnum / 2 ; i <= pnum ; i ++) {
		Node t = T.qry(i - (pnum >> 1) + 1 , i , 1 , pnum , 1) ;
		if(t.vfMpos != t.vcMpos) Ans = min(Ans , t.vfMax + t.vcMax) ;
		else Ans = min(Ans , max(t.vfMax + t.vcLax , t.vfLax + t.vcMax)) ;
	}
	for(int i = 1 ; i <= (pnum >> 1) ; i ++) findR(impp[i] , impp[i]) ;
	return (Ans / 2.0) ;
}
int main() {
	n = read() ;
	for(int i = 1 , u , v ; i <= n ; i ++) {
		double w ;
		u = read() , v = read() , w = read() ;
		add_edge(u , v , w) ;
		add_edge(v , u , w) ;
	}
	for(int i = 1 ; i <= n ; i ++)
	    if(!dfn[i])
	        tarjan(i , i) ;
	for(int i = 1 , u ; i <= pnum ; i ++) {
		u = impp[i] ;
		Ans = INF ;
		dp[u] = Dfs(u , u) ;
	}
	for(int i = pnum + 1 ; i <= pnum * 2 ; i ++) impp[i] = impp[i - pnum] ;
	pnum *= 2 ;
	printf("%.1lf\n",Solve()) ;
	return 0 ;
}
posted @ 2018-10-10 11:46  beretty  阅读(196)  评论(0编辑  收藏  举报