[SDOI2011]消防

题目描述

某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000)。

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

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

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

输入输出格式

输入格式:

输入包含n行:

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

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

输出格式:

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

输入输出样例

输入样例#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%的数据,n<=300。

对于50%的数据,n<=3000。

对于100%的数据,n<=300000,边长小等于1000。

可以發現,這條路只可能在直徑上,因此,我們只需在直徑上看選哪一段即可

我們可以用一個隊列加set維護每次儘量選長度迫近s的路,O(n)掃過去,總複雜度O(n),set的複雜度由於實在直徑上掃可忽略不計

當然還要預處理一些東西,從這個點網直徑外的其他點搜的最大長度,以這個點爲路徑起點前面的所有點到這個點的最大路經長,以這個點爲路徑終點後面的點到這個點的最大路徑長度,就可以了,可能有更簡潔的寫法,但是我一有思路就上了,代碼可能有些冗長,不過關鍵還是思路吧

#include<bits/stdc++.h>
#define ll long long
#define inf 999999999
#define mm(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn = 600010;
int n, s, Begin[maxn], to[maxn], Next[maxn], e, w[maxn];
int read(){
	int sum = 0,fg = 1;
	char c = getchar();
	while(c < '0' || c > '9'){if(c == '-')fg = -1;c = getchar();}
	while(c >='0' && c <='9')sum = (sum<<1) + (sum<<3) + c-'0',c = getchar();
	return sum * fg;
}
void add(int x,int y,int z){
	to[++e] = y;
	Next[e] = Begin[x];
	Begin[x] = e;
	w[e] = z;
}
int dis[maxn], max_dis, pos, fa[maxn], q[maxn], Max[maxn], p[maxn], id[maxn];
void dfs(int h,int father){
	for(int i = Begin[h];i ;i = Next[i]){
		int v = to[i];
		if(v != father){
			fa[v] = h;
			dis[v] = dis[h] + w[i];
			id[v] = i;
			dfs(v, h);
		}
	}
	if(dis[h] > max_dis)max_dis = dis[h], pos = h;
}
void _dfs(int h,int father,int tmp){
	for(int i = Begin[h];i ;i = Next[i]){
		int v = to[i];
		if(v != father && !p[v]){
			dis[v] = dis[h] + w[i];
			_dfs(v, h, tmp);
		}
	}
	Max[tmp] = max(Max[tmp], dis[h]);
}
int ls[maxn], cnt, nowmax[maxn], quan[maxn], _nowmax[maxn], ans = inf;
struct node{
	int value;
	node(){
		value = 0;
	}
};
set<node> se;
set<node>::iterator it;
bool operator < (node c,node d){
	return c.value > d.value;
}
void done(int tmp){
	int u = pos,v = tmp;
	while(u != v){
		p[u] = 1;
		u = fa[u];
	}
	p[u] = 1;
	u = pos, v = tmp;
	while(u != v){
		dis[u] = 0;
		_dfs(u, -1, u);
		u = fa[u];
	}
	dis[u] = 0;
	_dfs(u, -1, u);
	u = pos, v = tmp;
	while(u != v){
		ls[++cnt] = u;
		quan[cnt] = w[id[u]];
		u = fa[u];
	}
	ls[++cnt] = u;
	int t = Max[u];
	nowmax[u] = Max[u];
	for(int i = cnt-1;i >= 1; --i){
		int now = ls[i];
		t = max(t + w[id[now]], Max[now]);
		nowmax[now] = t;
	}
	t = Max[1];
	_nowmax[ls[1]] = t;
	for(int i = 2;i <= cnt; ++i){
		int now = ls[i];
		t = max(t + w[id[ls[i-1]]], Max[now]);
		_nowmax[now] = t;
	}
	int l = 1,r = 0,tot = 0,maxnow = 0;
	q[++r] = 1;
	int gross = max(_nowmax[tmp], nowmax[tmp]);
	ans = min(ans, gross);
	node T;
	T.value = Max[1];
	se.insert(T);
	for(int i = 2;i <= cnt; ++i){
		while(l <= r && tot + quan[i-1] > s){
			T.value = Max[ls[q[l]]];
			se.erase(T);
			tot -= quan[q[l]];
			++ l;
		}
		tot += quan[i-1];
		T.value = nowmax[ls[i]];
		se.insert(T);
		T.value = Max[ls[i]];
		se.insert(T);
		if(l <= r){
			T.value = _nowmax[ls[q[l]]];
			se.insert(T);
			it = se.begin();
			node tt = *it;
			ans = min(ans, tt.value);
			se.erase(T);
		}
		else{
			T.value = _nowmax[ls[i]];
			se.insert(T);
			it = se.begin();
			node tt = *it;
			se.erase(T);
			ans = min(ans, tt.value);
		}
		T.value = nowmax[ls[i]];
		se.erase(T);
		q[++r] = i;
	}
	printf("%d\n", ans);
}
void solve(){
	dis[1] = 0;
	dfs(1, -1);
	int tmp = pos;
	dis[tmp] = 0;
	max_dis = 0;
	fa[tmp] = 0;
	dfs(tmp, -1);
	done(tmp);
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("Thunder.in","r",stdin);
	freopen("Thunder.out","w",stdout);
#endif
	n = read(),s = read();
	for(int i = 1;i < n; ++i){
		int x = read(),y = read(),z = read();
		add(x, y, z), add(y, x, z);
	}
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值