「BZOJ 3242」「NOI 2013」快餐店「基環樹」
阿新 • • 發佈:2019-02-11
影響 oid 如果 bzoj type 就是 ans fin dfs
題意
基環樹上找到一個點(可以在邊上)使得它到樹上最遠點的距離最小,輸出最小距離
題解
如果是一棵樹,答案就是樹的直徑\(/2\)
如果是基環樹,那麽很好證明刪去環上的某一條邊是不影響答案的。於是斷環為鏈,單調隊列維護\(dep+sum,dep-sum\)的最大值和次大值,然後算直徑,如果兩個最大值是同個結點就取一個次大,否則都取最大。
#include <algorithm> #include <cstdio> using namespace std; typedef long long ll; const int N = 1e5 + 10; struct Edge { int v, w, nxt; } e[N << 1]; int hd[N], p; void link(int u, int v, int w) { e[p] = (Edge) {v, w, hd[u]}; hd[u] = p ++; } int n, dfn[N], idx; int fa[N], fw[N], c[N], d[N], cnt; bool cir[N]; void dfs(int u, int cur = -1) { dfn[u] = ++ idx; for(int i = hd[u]; ~ i; i = e[i].nxt) if(i != cur) { int v = e[i].v, w = e[i].w; if(!dfn[v]) { fa[v] = u; fw[v] = w; dfs(v, i ^ 1); } else if(dfn[v] < dfn[u]) { cnt ++; cir[c[cnt] = u] = 1; d[cnt] = w; for(int j = u; j != v; j = fa[j]) { cnt ++; cir[c[cnt] = fa[j]] = 1; d[cnt] = fw[j]; } } } } ll mdep[N][2], tree_d; void dfs2(int u, int f = -1) { for(int i = hd[u]; ~ i; i = e[i].nxt) { int v = e[i].v; if(v == f || cir[v]) continue ; dfs2(v, u); ll dis = e[i].w + mdep[v][0]; if(dis > mdep[u][0]) { mdep[u][1] = mdep[u][0]; mdep[u][0] = dis; } else if(dis > mdep[u][1]) { mdep[u][1] = dis; } } tree_d = max(tree_d, mdep[u][0] + mdep[u][1]); } int main() { scanf("%d", &n); fill(hd + 1, hd + n + 1, -1); for(int u, v, w, i = 1; i <= n; i ++) { scanf("%d%d%d", &u, &v, &w); link(u, v, w); link(v, u, w); } dfs(1); static ll dep[N << 1], sum[N << 1], ans = 1ll << 62; for(int i = 1; i <= cnt; i ++) { dfs2(c[i]); dep[i] = dep[i + cnt] = mdep[c[i]][0]; } for(int i = 1; i <= cnt << 1; i ++) sum[i] = sum[i - 1] + d[i > cnt ? i - cnt : i]; static int q1[N << 1], l1, r1; static int q2[N << 1], l2, r2; #define val1(u) dep[u] - sum[u] #define val2(u) dep[u] + sum[u] for(int i = 1; i <= cnt << 1; i ++) { for(; l1 < r1 && q1[l1] + cnt - 1 < i; l1 ++) ; for(; r1 - l1 > 1 && q1[l1 + 1] + cnt - 1 < i; l1 ++) q1[l1 + 1] = q1[l1]; for(; l2 < r2 && q2[l2] + cnt - 1 < i; l2 ++) ; for(; r2 - l2 > 1 && q2[l2 + 1] + cnt - 1 < i; l2 ++) q2[l2 + 1] = q2[l2]; for(; r1 - l1 > 2 && val1(q1[r1 - 1]) <= val1(i); r1 --) ; q1[r1 ++] = i; if(r1 - l1 <= 3) { for(int j = r1 - 1; j > l1; j --) if(val1(q1[j]) > val1(q1[j - 1])) swap(q1[j], q1[j - 1]); } for(; r2 - l2 > 2 && val2(q2[r2 - 1]) <= val2(i); r2 --) ; q2[r2 ++] = i; if(r2 - l2 <= 3) { for(int j = r2 - 1; j > l2; j --) if(val2(q2[j]) > val2(q2[j - 1])) swap(q2[j], q2[j - 1]); } if(i >= cnt && r1 - l1 > 1) { int a1 = q1[l1], a2 = q1[l1 + 1]; int b1 = q2[l2], b2 = q2[l2 + 1]; ll cir_d = 0; if(a1 == b1) cir_d = max(val1(a1) + val2(b2), val1(a2) + val2(b1)); else cir_d = val1(a1) + val2(b1); ans = min(ans, max(tree_d, cir_d)); } } printf("%lld.%c\n", ans >> 1, ans & 1 ? '5' : '0'); return 0; }
\(\text{Codeforces 835F}\)是一樣的題,數據範圍乘\(2\),改一下輸出就行.
什麽,\(\text{Wrong Answer}\)?
我覺得這是一個錯誤的解法,回頭有時間更新一種用前後綴的做法。
如果我幾個月都沒更 可以在評論裏捶我
「BZOJ 3242」「NOI 2013」快餐店「基環樹」