【题解】CF-100886K-Toll Roads

题目

http://codeforces.com/gym/100886/problem/K

题意

可以将边权均为 1 树上长度至多为 k 的一段的边权置为 0,求直径的最小值。

题解

  • 首先枚举修改的路径的一个端点,作为树形 dp 的根。
  • \(u\) 到根的路径被置为 0,那么这棵树的直径可能出现在
    • \(u\) 的子树中
    • \(u\) 的祖先中的某个不是 \(u\) 方向的儿子的子树中
    • 某两个 \(u\) 的祖先的不是 \(u\) 方向的儿子的最长链
  • 预处理每个子树中的最长链和最大直径
  • dp 的时候传递的祖先的信息是
    • 除去 \(u\) 的子树的剩下部分的直径
    • \(u\) 到除去 \(u\) 的子树剩下部分的最长链

代码(细节有点多)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
#ifdef zerol
#define dbg(args...) do { cout << "\033[32;1m" << #args << " -> "; err(args); } while (0)
#else
#define dbg(...)
#endif
void err() { cout << "\033[39;0m" << endl; }
template<template<typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args) { for (auto x: a) cout << x << ' '; err(args...); }
template<typename T, typename... Args>
void err(T a, Args... args) { cout << a << ' '; err(args...); }
// -----------------------------------------------------------------------------
const int N = 5E3 + 100, INF = 1E9;
vector<int> G[N];
pair<int, int> path;
int Ans = INF, L;
int f[N], g[N], dep[N]; // f: /\ g: /

void predfs(int u, int fa, int d = 0) {
dep[u] = d; f[u] = g[u] = 0;
for (int& v: G[u]) {
if (v == fa) continue;
predfs(v, u, d + 1);
f[u] = max(max(f[u], f[v]), g[u] + g[v] + 1);
g[u] = max(g[u], g[v] + 1);
}
dbg(u, f[u], g[u]);
}

int K;
void dfs(int u, int fa, int ff, int gg) {
if (dep[u] > K) return;
int cur = max(f[u], max(ff, gg + g[u]));
if (cur < Ans || (cur == Ans && dep[u] < L)) {
Ans = cur; L = dep[u]; path = {u, -1};
}
int f1 = 0, f2 = 0, g1 = -1, g2 = -1, g3 = -1;
for (int& v: G[u]) {
if (v == fa) continue;
if (f[v] > f2) f2 = f[v];
if (f2 > f1) swap(f1, f2);
if (g[v] > g3) g3 = g[v];
if (g3 > g2) swap(g2, g3);
if (g2 > g1) swap(g1, g2);
}
for (int& v: G[u]) {
if (v == fa) continue;
int t = g[v] == g1 ? g2 + g3 : (g[v] == g2 ? g1 + g3 : g1 + g2);
dfs(v, u,
max(max(f[v] == f1 ? f2 : f1, t + 2)
, max(ff, gg + 1 + (g[v] == g1 ? g2 : g1))),
max(gg, (g[v] == g1 ? g2 : g1) + 1));
}
}

int main() {
#ifdef zerol
freopen("in", "r", stdin);
#endif
int n; cin >> n >> K;
FOR (_, 1, n) {
int u, v; scanf("%d%d", &u, &v);
dbg(u, v);
G[u].push_back(v); G[v].push_back(u);
}
FOR (rt, 0, n) {
dbg(rt);
predfs(rt, -1);
dfs(rt, -1, 0, 0);
if (path.second == -1) path.second = rt;
}
cout << Ans << endl << L << endl;
if (L) cout << path.first << ' ' << path.second << endl;
}