E. Dog Snacks(Codeforces Round #688 (Div. 2))
time limit per test:3 seconds
memory limit per test:512 megabytes
judge: CF
题意
有一个 n n n 个节点的树,每个节点都有一些食物,Badugi要吃到所有节点的食物。一个节点的食物被吃掉后不再有食物。Badugi只能闻到距离不超过 k k k 的食物。
Badugi吃到所有食物后需要返回到根节点(编号 1 1 1 ),同样只有1距离Badugi不超过 k k k 时Badugi才能回到 1 1 1。
求出最小的 k k k 以使Badugi可以吃到所有节点的食物并返回根节点。
题解
定义 d p [ u ] dp[u] dp[u]:访问完 u u u 的子树后能停留的最小深度。
d p [ 叶 子 ] = 0 dp[叶子]=0 dp[叶子]=0
考虑一个策略:
- 对于根节点 1 1 1 来说,应把 d p dp dp 值最大的子树留作最后返回。因为如果不把 d p dp dp 值最大的留作最后,那么中间访问这个子树的时候就需要移动到根节点的另一个的儿子节点,中间就多了一步
u->另一个儿子
的路径,这个子树已经是 d p dp dp 值最大的了,再加一段路径会使之更大,不利于减小答案。 - 对于不是根节点的节点来说,应当把 d p dp dp 值最小的子树留作最后返回。假设我们枚举到了 u u u 节点,对于 u u u 的儿子来说,“返回”意味着到达 u u u 之后下一步要到父节点的另一个儿子,不执行“返回”的儿子节点到达 u u u 之后只需要移到 u u u 的另一个儿子节点就可以了。所以执行“返回”的子树的 d p dp dp 值要尽量小。
下面思考细节:
- 对于所有不是根节点的节点 u u u 来说
- 维护 d p [ u ] = m i n ( d p [ v ] ) + 1 dp[u] = min(dp[v]) + 1 dp[u]=min(dp[v])+1
- 当 u u u 有大于 1 1 1 个儿子节点的时候,遍历完 u u u 的一个儿子节点的子树后需要移动到另一个儿子的子树,路径长度为: d p [ v ] + 从 v 到 u + 从 u 到 u 的 另 一 个 儿 子 节 点 = d p [ v ] + 2 dp[v]+从 v 到 u +从 u 到 u 的另一个儿子节点=dp[v]+2 dp[v]+从v到u+从u到u的另一个儿子节点=dp[v]+2。让 a n s ans ans 与所有的儿子的 d p [ v ] + 2 dp[v]+2 dp[v]+2 取最大值。
- 当 u u u 只有 1 1 1 个儿子时,此时可以不用考虑对答案的影响,因为它的父亲节点会考虑。
- 对于根节点来说
- 由于需要从 d p dp dp 值最大的子树返回,所以 a n s ans ans 要与这个最大的 d p dp dp 值取最大值。
- d p dp dp 值最大的子树不需要移动到根节点的另一个儿子,所以我们对其他的子树的 d p [ v ] + 2 dp[v]+2 dp[v]+2 取最大值,也就是所有儿子的 d p [ v ] + 2 dp[v]+2 dp[v]+2 取不严格的次大值,对次大值与 a n s ans ans 取最大值。
由于我们枚举 u u u 时只需要用到 u u u 的儿子节点的 d p dp dp 值,所以我们可以不显式声明 d p dp dp 数组,而用函数的返回值代替 d p dp dp 值,降低内存开销。
代码
#include <bits/stdc++.h>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 200005;
const LL INF = 0x3f3f3f3f3f3f3f3f;
inline int read() {
int x(0), f(1); char ch(getchar());
while (ch<'0' || ch>'9') {
if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') {
x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n;
vector<int> G[maxn];
LL ans;
void init() {
ans = 0;
_rep(i, 1, n) G[i].clear();
}
LL dfs(int u, int fa) {
LL mi = INF, ma = 0, sema = 0;
int cnt = 0;
for(int v : G[u]) {
if(v == fa) continue;
++cnt;
LL t = dfs(v, u);
mi = min(mi, t);
if(t > ma) {
sema = ma;
ma = t;
}
else if(t > sema) sema = t;
}
if(cnt >= 2 && u != 1) ans = max(ans, ma + 2);
if(cnt >= 2 && u == 1) ans = max(ans, sema + 2);
if(u == 1) ans = max(ans, ma + 1);
if(cnt) return mi + 1;
else return 0;
}
void sol() {
init();
_for(i, n - 1) {
int u = read(), v = read();
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 1);
printf("%lld\n", ans);
}
int main() {
int T = read();
_for(i, T) {
n = read();
sol();
}
return 0;
}