题解 CF516D 【Drazil and Morning Exercise】
标签: 图论, 并查集
注意这个
Part 1
首先两遍 dfs 求出每个点的
Part 2
观察发现距离某个点最远的点组成的点集必然包含每一条直径的两个端点中至少一个. 所以显然以任意一条直径的中点作为树根, 每一个点的
根据这个单调性我们可以用尺取(也即 two points)来做了.
#include <bits/stdc++.h>
using namespace std;
long long read();
void chmax(long long &x, long long y) { x < y ? x = y : x; }
int n, q;
struct E {
int v, w;
};
vector<E> e[200005];
void add(int f, int t, int w) {
e[f].push_back((E){t, w}), e[t].push_back((E){f, w});
}
long long f[200005], g[200005];
void update(int u, long long d) {
chmax(g[u], d), (g[u] > f[u]) ? swap(g[u], f[u]) : void();
}
void dfs1(int u, int fa) {
for (int i = 0, v; i < e[u].size(); ++i)
if ((v = e[u][i].v) != fa) dfs1(v, u), update(u, f[v] + e[u][i].w);
}
int up[200005], p[200005];
bool cmp(int u, int v) { return f[u] == f[v] ? u < v : f[u] < f[v]; }
void dfs2(int u, int fa) {
for (int i = 0, v; i < e[u].size(); ++i) {
if ((v = e[u][i].v) == fa) continue;
update(v, e[u][i].w + ((f[v] + e[u][i].w == f[u]) ? g[u] : f[u]));
dfs2(v, u);
}
for (int i = 0, v; i < e[u].size(); ++i)
cmp(u, v = e[u][i].v) ? up[v] = u : up[u] = v;
}
int fa[200005], w[200005];
int find(int u) { return fa[u] == u ? u : fa[u] = find(fa[u]); }
void work(long long d) {
int res = 0;
for (int i = 1; i <= n; ++i) fa[i] = i, w[i] = 1;
for (int i = n, j = n; i >= 1; --i) {
while (f[p[j]] > f[p[i]] + d) --w[find(p[j--])];
res = max(res, w[p[i]]), w[fa[p[i]] = up[p[i]]] += w[p[i]];
}
printf("%d\n", res);
}
int main() {
n = read();
for (int i = 1, u, v; i < n; ++i) u = read(), v = read(), add(u, v, read());
dfs1(1, 0), dfs2(1, 0), q = read();
for (int i = 1; i <= n; ++i) p[i] = i;
sort(p + 1, p + 1 + n, cmp);
while (q--) work(read());
return 0;
}
const int _SIZE = 1 << 22;
char ibuf[_SIZE], *iS = ibuf, *iT = ibuf;
#define gc \
(iS == iT ? iT = ((iS = ibuf) + fread(ibuf, 1, _SIZE, stdin)), \
(iS == iT ? EOF : *iS++) : *iS++)
long long read() {
long long x = 0, f = 1;
char c = gc;
while (!isdigit(c)) f = (c == '-') ? -1 : f, c = gc;
while (isdigit(c)) x = x * 10 + c - '0', c = gc;
return x * f;
}