CF1632E2 题解

· · 题解

CF1632E1&E2 题解

题意

给一棵初始边权为 1 的树,对于所有 x\in[1,n],求:

若能添一条权为 x 的无向边 (a,b),则 \max\limits_{i=1}^n(d_i) 的最小值是多少,

其中 d_i 等于 点 1 到点 i 的最短路径的长度。

做法

先考虑对于固定 x 后如何计算答案。

首先,问题可以转化为,我们添加的边至少有一个端点是 1

因为,对于一张加了边 u\leftrightarrow v(u,v>1) 的新图 A,此处我们假设 d_u\leq d_v

则加了边 1\leftrightarrow v 的新图 B 一定更优。我们通过枚举 A 中的决策来证明:

任意一条 A 中的路径 p=1\rightarrow\cdots\rightarrow t

p 中不包含新边 u\leftrightarrow v,则 B 方式中也存在路径 q=p

而若 p 中包含新边 u\leftrightarrow v,即 p=1\rightarrow\cdots\rightarrow u\rightarrow v \rightarrow s\rightarrow\cdots\rightarrow t

我们能在 B 方式中找到路径 q=1\rightarrow v \rightarrow s\rightarrow\cdots \rightarrow t

其中 q 中经过了新边 1\rightarrow v,且 q 的长度必然不大于 p 的长度。

那么我们考虑新问题的做法,这时我们考虑二分答案,那么问题就又转化为:

对于值 ans,是否存在一点 v,使得加入边 1\leftrightarrow v 后的新图 G\max\limits_{i=1}^n(d_i) 的最小值不大于 ans

那么,原树里 d_u>ans 的点 u 都必须被新边 1\leftrightarrow v 影响而使得 d'_u\le ans

其中 d'_u 是加边后点 1 到点 u 的距离。

再考虑新家的边是如何影响 d 数组的变化的,即加入权为 x 的新边 1\leftrightarrow v 后,我们有:

那么,对于所有满足 $d_u>ans$ 的点 $u$,右式 $dist(v,u)\le ans-x$ 都必须成立。 我们发现,这个 $ans-x$ 的值,与 $u$ 具体是哪个点并没有关系, 故我们只需让 $\max\limits_{u\in[1,n]}(dist(u,v))$ 尽量小即可,而这是一个经典模型, 求解这个模型,我们只需找到一条最长的路径 $a\rightarrow\cdots\rightarrow b$,满足 $d_a,d_b>ans$, 设这条路径的长度是 $len$,则答案 $ans$ 可行当且仅当右式 $x+\lfloor\frac{len+1}{2}\rfloor\le ans$ 成立。 原因是,我们只需将点 $v$ 放在这条路径的中点处, 则对 $\forall u\in[1,n]$,只要满足 $d_u>ans$,就有 $dist(v,u)\le x+\lfloor\frac{len+1}{2}\rfloor$, 否则一定能找到一条新的长为 $len'$ 的路径 $a'\rightarrow\cdots\rightarrow b'$,满足 $d_{a'},d_{b'}>ans$, 其中 $u$ 等于 $a'$ 和 $b'$ 中的一个。 那最后一个问题就是,对于一个固定的 $ans$,如何求上面的 $len$ 值。 这个倒不难,可以一次预处理出所有 $ans$ 对应的 $len$,方法可以采用树形DP或找直径后直接计算。 时间复杂度是 $O(n\log n)$,瓶颈在二分上。 当然,因为答案的值域也是 $O(n)$ 的,而答案会随着新边边权 $x$ 的上升而上升, 故可以维护一个当前答案 $t$,每次 $x$ 增加时同时维护 $t$ 的值,这样的时间复杂度就是线性的。 **code for** $O(n\log n)$: ``` #define vi vector #define ckmax(a, b) ((a) = max((a), (b))) #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define per(i, a, b) for (int i = (a); i >= (b); i--) int read () { /*快读*/ } const int N (3e5 + 10); int n, rt; int f[N]; int d[2][N]; vi < int > G[N]; void dfs (int u, int fa, int o) { if (fa) d[o][u] = d[o][fa] + 1; for (int v : G[u]) if (v ^ fa) dfs (v, u, o); } bool chk (int ans, int x) { return ans >= min (d[0][rt], x + (f[ans + 1] + 1) / 2); } void work() { n = read(); int u, v; f[0] = 0; rep (i, 1, n) G[i].clear(), f[i] = d[0][i] = d[1][i] = 0; rep (i, 2, n) G[u = read()].pb (v = read()), G[v].pb (u); dfs (1, 0, 0), rt = max_element (d[0] + 1, d[0] + n + 1) - d[0], dfs (rt, 0, 1); rep (i, 1, n) ckmax (f[d[0][i]], d[1][i]); per (i, n - 1, 0) ckmax (f[i], f[i + 1]); rep (x, 1, n) { int L = 0, R = n, ans = n; while (L <= R) { int Mid = (L + R) >> 1; if (chk (Mid, x)) ans = Mid, R = Mid - 1; else L = Mid + 1; } printf ("%d ", ans); } puts(""); } int main() { int t = read(); while (t--) work(); return 0; } ```