[NOI2013]快餐店(基環樹dp)
阿新 • • 發佈:2020-12-10
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; }