CF1632E1&E2 题解
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\) 后,我们有:
\(\forall u \in[1,n],d'_u=\min(d_u,x+\text{dist}(v,u))\),其中 \(dist(v,u)\) 为原树中 \(v\) 到 \(u\) 的距离。
那么,对于所有满足 \(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;
}