CF1632E2 题解
GaryH
·
·
题解
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;
}
```