NOIP2018 提高组 赛道修建
贪心,双指针
算法 1
对于 \(m=1\),只有一条赛道,也就是树的直径。
算法 2
对于 \(b_i=a_i+1\),此时树退化为链,问题转化为将 \(n-1\) 长度的序列 \(a\) 找出 \(m\) 段,使得最小段长度最大。
算法 3
对于 \(a_i=1\),菊花图的情况。思路开始接近正解。其实我们也可以看成是一个序列,只能取 \(1 \sim 2\) 条。先将边长从大到小排序,对于最前面的,长度 \(\geq x\)(\(x\) 为我们二分出的长度)的,记 \(+1\) 后跳过,剩下的我们需要选 \(2\) 条构成一个赛道。显然,假设最左边的是 \(l\),我们希望找到一个 \(r\),满足 \(l+r \geq x\) 且 \(r\) 最小。因为已经排序,所以从右往左逐渐变大,于是从右往左开始走,直到遇到 \(r\) 满足要求。后面 \(l\) 会不断缩小,\(r\) 就应该随着增大,所以 \(r\) 不能往回走。当我们找不到 \(r\) 时结束,判断是否有 \(m\) 条赛道。时间复杂度为 \(O(n \log n+n)\)。
到此,可以得到 \(55\) 分。
算法 4
对于 \(100\%\) 的数据。
考虑每棵子树的贡献。可以发现,如果我们不看子树的根节点到其父节点的边,与 \(a_i=1\) 时是相同的。然后我们尽可能选出一条最长的边,这条边作为该子树的贡献往上合并,且保证赛道数量不变,否则贡献为 \(0\)。时间复杂度为 \(O(n \log n \log w)\)。
#include <bits/stdc++.h>
const int N = 5e4 + 5;
int n, m, tot;
std::vector<std::pair<int, int>> g[N];
int dfs(int u, int fa, int x) {
std::vector<int> tmp;
for (auto i : g[u]) {
int v, w;
std::tie(v, w) = i;
if (v == fa) {
continue;
}
int z = dfs(v, u, x);
if (z != -1) {
tmp.push_back(z + w);
} else {
tmp.push_back(w);
}
}
int max = 0;
std::sort(tmp.begin(), tmp.end(), std::greater<int>());
int k = tmp.size();
for (int i = 0, j = k - 1; i < k; i++) {
if (tmp[i] >= x) {
max++;
continue;
}
while (i < j && tmp[i] + tmp[j] < x) {
j--;
}
if (i >= j) {
break;
}
max++;
j--;
}
tot += max;
int l = 0, r = k - 1, ans = -1;
while (l <= r) {
int mid = (l + r) >> 1;
int res = 0;
for (int i = 0, j = k - 1; i < k; i++) {
if (i == mid) {
continue;
}
if (tmp[i] >= x) {
res++;
continue;
}
while (i < j && tmp[i] + tmp[j] < x) {
j--;
}
if (j == mid) {
j--;
}
if (i >= j) {
break;
}
res++;
j--;
}
if (res == max) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return ans == -1 ? ans : tmp[ans];
}
bool check(int x) {
tot = 0;
dfs(1, 0, x);
return tot >= m;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
for (int i = 1; i < n; i++) {
int u, v, w;
std::cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
int l = 0, r = 5e8, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
std::cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号