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;
}
posted @ 2022-02-03 17:04  GaryH  阅读(40)  评论(0编辑  收藏  举报