From ec3c08099fa8a37b48bf0cc4b911b001f0606c08 Mon Sep 17 00:00:00 2001 From: hanyixuanten Date: Tue, 27 Oct 2026 12:41:49 +0000 Subject: [PATCH] modified: solutions/P17437.md --- solutions/P17437.md | 170 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 15 deletions(-) diff --git a/solutions/P17437.md b/solutions/P17437.md index 217e5cd..9fd42f6 100644 --- a/solutions/P17437.md +++ b/solutions/P17437.md @@ -10,26 +10,50 @@ 先枚举第一条断边 $u$,会将树分为 $u$ 的子树和剩下部分 $A, B$,第二刀只能落在 $A$ 或 $B$ 内部。问题变成:**固定第一刀后,在某一侧内部再切一刀,使三块中位数最大。** -## 命题:第二刀切出的一定是 $A$ 或 $B$ 的最大或最小子树 +## 命题:对于全局最优的两条切边,总存在其中一条边作为第一刀,使得最优三块的中位数可以由 dmin/dmax 或 umin/umax 四类候选之一达到。 -假设在 $A$ 中切了一刀,分为 $a, A - a$ +::::error[错误命题] -设 $M(a) = median(a, A - a, B)$,考虑 $a \in (0, \frac{A}{2}]$。考虑 $a$ 从 $0 - \frac{A}{2}$ 的过程中。 +先反驳一下一个结论:第二刀切出的一定是 $A$ 或 $B$ 的最大或最小子树。 -$$ -M(a) = -\begin{cases} -A - a, & B \ge A - a \\ -B, & a \le B \le A - a \\ -a, & B \le a -\end{cases} -$$ +带入数据: -$M(a)$ 的变化情况如图。 +```plaintext +6 +0 1 3 2 3 1 +1 2 +2 3 +3 4 +4 5 +5 6 +``` -![图](https://cdn.luogu.com.cn/upload/image_hosting/8l8gqxsl.webp) +发现第一刀切 $1, 2$ 时,$dmax=9, dmin=1$。但是实际上这种情况下最优解为 $a = 4$ 时,$median = 4$ -其中三种不同颜色的框分别代表三个区间。因此 $median$ 的最大值只可能在 $a \rightarrow 0$ 或 $a \rightarrow \frac{A}{2}$ 时取到。对于第二刀切在 $B$ 中时也是类似的。其中,$a \rightarrow 0$ 表示要切出最小子树,而 $a \rightarrow \frac{A}{2}$ 表示 $a, A - a$ 尽可能均衡,此时 $B$ 要尽可能小才能使得中位数最大。 +但是按照这个命题实现的答案却是正确的,原因就是[本命题](#命题对于全局最优的两条切边总存在其中一条边作为第一刀使得最优三块的中位数可以由-dmindmax-或-uminumax-四类候选之一达到) + +:::: + +::::success[证明] + +设全局最优三块权值排序为 $x\le y\le z$,其中 $y$ 就是最优中位数。注意以下论证不依赖权值非负。 + +记第一刀切出的一侧为 $A$,另一侧为 $B$。若第二刀在 $A$ 内切出块 $a$,三块为 $a,A-a,B$;若在 $B$ 内切出块 $b$,三块为 $A,b,B-b$。由定义,对 $A$ 内任意可切块 $a$ 有 $dmin_A\le a\le dmax_A$;对 $B$ 内任意可切块 $b$ 有 $umin_B\le b\le umax_B$。 + +分两类讨论: + +1. **两个切点互不为祖先**。设两棵互不相交的子树为 $Q,R$,补集为 $P$,权和分别为 $p,q,r$。 + - 若中位块是某棵子树(例如 $Q$,此时 $q=y$),取切出 $Q$ 的边为第一刀,则 $A=Q=y$,$B=P+R$,第二刀在 $B$ 内切出 $R$。若 $r\le y$,取 $umin_B\le r\le y$,则 $B-umin_B\ge B-r=P\ge y$,中位数为 $y$;若 $r\ge y$,取 $umax_B\ge r\ge y$,则 $B-umax_B\le P\le y$,中位数仍为 $y$。 + - 若中位块是补集 $P$(此时 $p=y$),$q,r$ 一个不大于 $y$、一个不小于 $y$。取权值不小于 $y$ 的那棵子树对应的边为第一刀(不妨设为 $R$,则 $A=R\ge y$,$B=P+Q$),第二刀在 $B$ 内切出 $Q$。取 $umin_B\le q\le y$,则 $B-umin_B\ge B-q=P=y$,三块中位数不小于 $y$,由全局最优性为 $y$。 + +2. **两个切点有祖先关系**。设祖先边切出 $U$,后代边在 $U$ 内切出 $V$,三块为 $P=T\setminus U$、$Q=U\setminus V$、$R=V$,权和为 $p,q,r$。 + - 若中位块是 $R$($r=y$),取后代边为第一刀,$A=R=y$,$B=P+Q$,第二刀在 $B$ 内切出 $P$。若 $p\le y$,取 $umin_B\le p\le y$,则 $B-umin_B\ge Q\ge y$;若 $p\ge y$,取 $umax_B\ge p\ge y$,则 $B-umax_B\le Q\le y$。中位数为 $y$。 + - 若中位块是 $P$($p=y$),取祖先边为第一刀,$A=Q+R$,$B=P=y$,第二刀在 $A$ 内切出 $R$。若 $r\le y$,取 $dmin_A\le r\le y$,则 $A-dmin_A\ge Q\ge y$;若 $r\ge y$,取 $dmax_A\ge r\ge y$,则 $A-dmax_A\le Q\le y$。中位数为 $y$。 + - 若中位块是 $Q$($q=y$),则 $p,r$ 一个不大于 $y$、一个不小于 $y$。若 $r\le y\le p$,取祖先边为第一刀,$A=Q+R$,$B=P$,第二刀在 $A$ 内切出 $R$,取 $dmin_A\le r\le y$,则 $A-dmin_A\ge Q=y$ 且 $B=P\ge y$,中位数不小于 $y$,故为 $y$;若 $p\le y\le r$,取后代边为第一刀,$A=R$,$B=P+Q$,第二刀在 $B$ 内切出 $P$,取 $umin_B\le p\le y$,则 $B-umin_B\ge Q=y$ 且 $A=R\ge y$,中位数亦为 $y$。 + +综上,全局最优中位数总可由“枚举一条边作为第一刀,再在另一侧取 $dmin/dmax$ 或 $umin/umax$”的四类候选之一达到。故最终枚举第一刀并检查四类候选即可。 + +:::: ## 预处理子树最大、最小可切值 @@ -111,4 +135,120 @@ $$ 记录最大答案即可。 -[code](../P17437.cpp) +::::info[code] + +```cpp +#include +#define int long long +using namespace std; +int n; // 5e5 +vector fri[500005]; +int dep[500005]; +int fa[500005]; +long long sum[500005]; +long long dmin[500005], dmax[500005]; // down max / min +long long umin[500005], umax[500005]; // up 反向的 max和min +int ord[500005]; +const long long INF = LLONG_MAX; +const long long _INF = LLONG_MIN; +long long median(long long x, long long y, long long z) { + long long a[3] = {x, y, z}; + sort(a, a + 3); + return a[1]; +} +int noword; +void dfs(int u) { + dep[u] = dep[fa[u]] + 1; + ord[++noword] = u; + for (int v : fri[u]) { + if (v == fa[u]) continue; + fa[v] = u; + dfs(v); + } +} +void pre() { + noword = 0; + dfs(1); + int cnt = noword; + for (int i = cnt; i >= 1; --i) { + int now = ord[i]; + dmin[now] = INF; + dmax[now] = _INF; + for (int v : fri[now]) { + if (v == fa[now]) continue; + sum[now] += sum[v]; + if (sum[v] < dmin[now]) dmin[now] = sum[v]; + if (sum[v] > dmax[now]) dmax[now] = sum[v]; + if (dmin[v] < dmin[now]) dmin[now] = dmin[v]; + if (dmax[v] > dmax[now]) dmax[now] = dmax[v]; + } + } + umin[1] = INF; + umax[1] = _INF; + for (int i = 1; i <= cnt; ++i) { + int u = ord[i]; + long long min1 = INF, min2 = INF; + long long max1 = _INF, max2 = _INF; + int minid = 0, maxid = 0; + for (int v : fri[u]) { + if (v == fa[u]) continue; + long long mn = sum[v]; + if (dmin[v] < mn) mn = dmin[v]; + long long mx = sum[v]; + if (dmax[v] > mx) mx = dmax[v]; + if (mn < min1) { + min2 = min1; + min1 = mn; + minid = v; + } else if (mn < min2) { + min2 = mn; + } + if (mx > max1) { + max2 = max1; + max1 = mx; + maxid = v; + } else if (mx > max2) { + max2 = mx; + } + } + long long pmin = INF, pmax = _INF; + if (u != 1) { + pmin = sum[1] - sum[u]; + if (umin[u] < pmin) pmin = umin[u]; + pmax = sum[1] - sum[u]; + if (umax[u] > pmax) pmax = umax[u]; + } + for (int v : fri[u]) { + if (v == fa[u]) continue; + long long amin = (v == minid ? min2 : min1); + long long amax = (v == maxid ? max2 : max1); + umin[v] = pmin < amin ? pmin : amin; + umax[v] = pmax > amax ? pmax : amax; + } + } +} +signed main() { + scanf("%lld", &n); + for (int i = 1; i <= n; ++i) + scanf("%lld", &sum[i]); + for (int i = 1, u, v; i < n; ++i) + scanf("%lld%lld", &u, &v), fri[u].push_back(v), fri[v].push_back(u); + pre(); + long long ans = _INF; + for (int v = 2; v <= n; ++v) { + long long B = sum[1] - sum[v]; + if (dmin[v] != INF) + ans = max(ans, median(dmin[v], sum[v] - dmin[v], B)); + if (dmax[v] != _INF) + ans = max(ans, median(dmax[v], sum[v] - dmax[v], B)); + if (umin[v] != INF) + ans = max(ans, median(umin[v], B - umin[v], sum[v])); + if (umax[v] != _INF) + ans = max(ans, median(umax[v], B - umax[v], sum[v])); + } + printf("%lld\n", ans); + return 0; +} +``` + +:::: \ No newline at end of file