标签:int http $1 路径 tle 怎么 ima 情况 解释
描述
给一棵 $n$ 个点的树,找到三个点,使得两两之间,共三条路径,所经过的边的并集尽可能大。
输入
第一行为一个正整数 $n$($3 \le n \le 2 \times 10^5$)。
接下来 $n -1 $ 行,每行两个数 $u,v$ 表示一条边($1 \le u, v \le n$,$u \neq v$)。
输出
第一行为一个数 $res$,表示并集的大小。
第二行为三个数 $a, b, c$,表示找的三个点,输出任意解($a \neq b$,$b \neq c$,$a \neq c$)。
样例
输入
8
1 2
2 3
3 4
4 5
4 6
3 7
3 8输出
5
1 8 6解释
这是另一种可行解。$1 \to 5$ 经过 $(1, 2)$,$(2, 3)$,$(3, 4)$,$(4, 5)$;$1 \to 6$ 经过 $(1, 2)$,$(2, 3)$,$(3, 4)$,$(4, 6)$;$5 \to 6$ 经过 $(4,5)$,$(4,6)$。并集是 $(1, 2)$,$(2, 3)$,$(3, 4)$,$(4, 5)$,$(4, 6)$ 共 $6$ 条边。
介绍一种自己的乱搞做法。
首先设选取的三个点分别为 $du, dv, dw$。
我们可以发现,三条路径的并集可以看作是有唯一的一个交点,例如样例:

交点为 $4$。
那么,问题就变成了,找到一个 $u$ 作为交点,$du, dv, dw$ 一定是到 $u$ 距离最远的,且不在同一棵子树里 的三个点。
为什么不在同一棵子树里?
因为如果 $du, dv$ 在一棵子树里,显然,我们可以找到它们分叉的地方,比如叫做 $v$,那 $u \to v \to du$ 和 $u \to v \to dv$ 两条路径就有 $u \to v$ 这一段相交的地方,那么交点就大可以设成 $v$ 了。
于是 $\mathcal O(n^2)$ 的做法不难想到:枚举 $u$,$n$ 次 BFS,暴力求出最远点更新答案。
这个做法显然是会 TLE 的,正解需要下面这个结论:
$du, dv$ 一定是树的直径的两个端点。
为什么呢?
比如我们有这么一棵树,它的直径用红色标出。

分两种情况:
那么我们要证明的就是最远的两条链 $du, dv$ 一定是树的直径的端点。

比如我们枚举到了这样的 $u$,我们发现 $u$ 除了直径还有一棵子树,这个里面离 $u$ 最远的叫做 $w$。
那么我们有:
$$ dis(u, a) \ge dis(u, w) \cdots \cdots (1) \\ dis(u, b) \ge dis(u, w) \cdots \cdots (2) $$

$(1)$ 是因为,如果有 $dis(u, w) > dis(u, b)$ 了,那么树的直径就不可能是 $a \to b$,而是 $a \to w$ 了;$(2)$ 也同理可证。
那么,$u$ 除了子树里面的点,也就 $a, b$ 两个选择了,所以 $du = a$,$dv = b$。

我们还是设 $u$ 的子树(这次有两棵)离 $u$ 最远的点为 $w$。再设 $u$ 不断沿着父亲追溯到直径上的点为 $v$。
同上,可以得到:
$$ dis(v, a) \ge dis(v, w) = dis(u, v) + dis(u, w) \\ dis(v, b) \ge dis(v, w) = dis(u, v) + dis(u, w) $$

右边去掉一个 $dis(u, v)$ 不影响不等式,所以:
$$ dis(v, a) \ge dis(u, w) \\ dis(v, b) \ge dis(u, w) $$
左边再加上一个 $dis(u, v)$ 也不影响不等式,所以:
$$ dis(v, a) + dis(u, v) = dis(u, a) \ge dis(u, w) \\ dis(v, b) + dis(u, v) = dis(u, b) \ge dis(u, w) $$
所以我们还是会先选 $du = a$,$dv = b$!
然后既然这样了,我们就可以先找到直径,于是 $du, dv$ 就出来了。
然后记录直径上的点为 $\text{path}$,在 $\text{path}$ 上枚举 $u$,找到「不在直径上」且 「距离 $u$ 最远」的点 $w$,用来更新答案。
具体怎么实现呢?我们可以把直径拉出来,比如样例,$1 \to 5$ 是直径,那么我们就:

把直径拉出来,然后我们从上至下记录为 $\text{path}_{1 \to len}$,我们发现,只要 $\text{path}_i$ 的儿子不是 $\text{path}_{i + 1}$,就是可以更新的。
容易设计出一个树形 DP,$f_{u}$ 表示 $u$ 最佳的长度,$g_{u}$ 表示来源,显然:
$\textbf{if } u \not \in \text{path } \textbf{then } f_u = \max\limits_{v \in \text{sons(u)}} \{f_v +1\}$
$\textbf{else } \text{let } u \text{ be path}_i, f_u = \max\limits_{v \in \text{sons(u)}, v \text{ isn‘t path}_{i + 1} }\{ f_v +1\}$
$g_u$ 相应更新。
时间复杂度 $\mathcal O(n)$,代码如下,仅供参考:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, dep[N], fa[N], du, dv, dlen;
vector<int> G[N];
void dfs(int u) // 预处理
{
for(int v : G[u]) if(v ^ fa[u])
{
dep[v] = dep[u] + 1;
fa[v] = u;
dfs(v);
}
}
int tmp[N], cur, path[N];
void find_path(int u) // 拉出直径
{
tmp[++cur] = u;
if(u == dv)
{
for(int i = 1; i <= cur; i++) path[i] = tmp[i];
return;
}
for(int v : G[u]) if(v ^ fa[u]) find_path(v);
cur--;
}
int f[N], g[N], maxl = -1, dw;
void solve(int u, int pos) // DP
{
if(u ^ path[pos])
{
for(int v : G[u]) if(v ^ fa[u])
{
solve(v, pos);
if(f[u] < f[v] + 1)
f[u] = f[v] + 1, g[u] = g[v];
}
}
else
{
for(int v : G[u]) if(v ^ fa[u])
{
solve(v, pos + 1);
if(v ^ path[pos + 1] && f[u] < f[v] + 1)
f[u] = f[v] + 1, g[u] = g[v];
}
}
if(f[u] > maxl && g[u] != du && g[u] != dv) // 注意,如果来源是 du 或 dv 就重复了,所以不行
maxl = f[u], dw = g[u];
}
int main()
{
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dep[1] = fa[1] = 0;
dfs(1);
for(int i = 1; i <= n; i++) if(dep[i] > dep[du]) du = i;
dep[du] = fa[du] = 0;
dfs(du);
for(int i = 1; i <= n; i++) if(dep[i] > dep[dv]) dv = i;
dlen = dep[dv];
find_path(du);
cur = n;
for(int u = 1; u <= n; u++) g[u] = u;
solve(du, 1);
cout << maxl + dlen << endl;
cout << du << ‘ ‘ << dv << ‘ ‘ << dw << endl;
return 0;
}
CodeForces 1294F Three Paths on a Tree
标签:int http $1 路径 tle 怎么 ima 情况 解释
原文地址:https://www.cnblogs.com/syksykCCC/p/CF1294F.html