1. 程式人生 > 實用技巧 >[NOI2013]快餐店(基環樹dp)

[NOI2013]快餐店(基環樹dp)

Analysis

這是一道類似於基環樹直徑的題。顯然答案為直徑除以2。

考慮暴力的做法,每次斷一條環上邊,然後找一下當前的直徑,再取一個最小值即可。

優化也很簡答,維護四個陣列 \(h1,h2,g1,g2\) 分別代表到左端點(環上第一個點)字首最大值,到右端點(也是環上第一個點(嘿嘿想不到吧,只不過饒了一圈))字尾最大值,以及字首最大答案和字尾最大答案,不動的看一下圖就懂了。

然後我們列舉斷邊\(i \rightarrow i+1\),然後拿\(max(h1[i]+h2[i+1],max(g1[i],g2[i+1]))\)來更新答案,別忘了再和不在環上的鏈取最大。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 100005;
const ll inf = 0x7f7f7f7f7f7f7f7f;
template <typename T> void read(T &x) {
	T f = 1;
	char ch = getchar();
	for (; '0' > ch || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
	for (x = 0; '0' <= ch && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
ll n;
vector<pair<ll, ll> > vec[MAXN];
bool vis[MAXN], mark[MAXN];
ll fa[MAXN];
ll stk[MAXN], top;
ll val[MAXN];
ll s, t;
ll dp[MAXN];
ll ans;
ll zsyy = inf;
void zsy(ll x) {
	vis[x] = true;
	stk[++top] = x;
	for (ll i = 0; i < (ll)vec[x].size(); i++) {
		ll y = vec[x][i].first;
		if (!mark[y]) continue;
		val[top] = vec[x][i].second;
		if (!vis[y]) zsy(y);
	}
}
void dpp(ll x) {
	ll fir = 0, sec = 0;
	for (ll i = 0; i < (ll)vec[x].size(); i++) {
		ll y = vec[x][i].first;
		if (mark[y]) continue;
		if (y == fa[x]) continue;
		fa[y] = x;
		dpp(y);
		dp[x] = max(dp[x], dp[y] + vec[x][i].second);
		if (dp[y] + vec[x][i].second > fir) {
			sec = fir;
			fir = dp[y] + vec[x][i].second;
		} else if (dp[y] > sec) {
			sec = dp[y] + vec[x][i].second;
		}
	}
	ans = max(ans, sec + fir);
}
bool flag;
void dfs(ll x) {
	if (flag) return;
	vis[x] = true;
	for (ll i = 0; i < (ll)vec[x].size(); i++) {
		ll y = vec[x][i].first;
		if (y == fa[x]) continue;
		if (flag) return;
		if (vis[y]) {
			flag = true;
			s = x;
			t = y;
			return;
		} else {
			fa[y] = x;
			dfs(y);
		}
		
	}
}
ll h1[MAXN], h2[MAXN], g1[MAXN], g2[MAXN];
int main() {
	read(n);
	for (ll i = 1; i <= n; i++) {
		ll a, b, l;
		read(a); read(b); read(l);
		vec[a].push_back(make_pair(b, l));
		vec[b].push_back(make_pair(a, l));
	}
	dfs(1);
	while (s != t) {
		mark[s] = 1;
		s = fa[s];
	}
	mark[t] = 1;
	memset(fa, 0, sizeof(fa));
	memset(vis, 0, sizeof(vis));
	for (ll i = 1; i <= n; i++) {
		if (mark[i]) {
			zsy(i);
			break;
		}
	}
	for (ll i = 1; i <= n; i++) {
		if (mark[i]) {
			dpp(i);
		}
	}
	ll sum = val[1], cur = dp[stk[1]] + val[1];
	for (ll i = 2; i <= top; i++) {
		h1[i] = max(h1[i - 1], dp[stk[i]] + sum);
		g1[i] = max(g1[i - 1], dp[stk[i]] + cur);
		cur = max(cur, dp[stk[i]]) + val[i];
		sum += val[i];
	}
	sum = val[top], cur = dp[stk[1]] + val[top];
	for (ll i = top; i > 1; i--) {
		h2[i] = max(h2[i + 1], dp[stk[i]] + sum);
		g2[i] = max(g2[i + 1], dp[stk[i]] + cur);
		cur = max(cur, dp[stk[i]]) + val[i - 1];
		sum += val[i - 1];
	}
	for (ll i = 1; i <= top; i++) {//列舉斷開 (i- -> i+1) 的邊 
		zsyy = min(zsyy, max(ans, max(h1[i] + h2[i + 1], max(g1[i], g2[i + 1]))));
	}
	printf("%.1lf", (double)zsyy / 2);
	return 0;
}