Description
给出一棵 n n n 个点的树,给出 n − 1 n-1 n−1 条 x x x 和 y y y 之间的边。
每条边有一个长度 v v v,询问 q q q次,每次询问给出一个 L L L,请选择一个符合要求的最大联通块,要求该联通块中所有点到树上离它最远的点的距离的最大值与最小值之差小于等于 L L L,输出联通块的最大大小。
n ≤ 100000 , q ≤ 50 n \leq 100000, q \leq 50 n≤100000,q≤50
Solution
q ≤ 50 q \leq 50 q≤50,显然没啥用。
设 d ( i ) d(i) d(i)表示 i i i到树上离它最远的点的距离。显然这个直接预处理。
然后枚举从大到小枚举最小的
d
d
d值
t
t
t,维护满足
d
≤
t
+
l
i
m
d \leq t+lim
d≤t+lim点形成的联通块大小,lct即可
上面那个方法太难写了(而且不太能过),考虑挖掘一些别的性质,可以发现树上离一个点最远的点只会是直径的两端点,那么如果以直径的中点为根(可以发现这是 d d d最小的点),可以发现每个点子树里的 d d d值都不小于子树根的 d d d值。
有了这个性质就很好做了,从大到小枚举最小的 d d d值 x x x,对于 d d d值超过 d + l i m d+lim d+lim的点直接删掉,因为它的子树肯定已经被删掉了,所以不会影响连通性。所以可以用并查集维护。时间复杂度 O ( n + n q α ( n ) ) O(n+nq\alpha(n)) O(n+nqα(n))
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
inline ll gl()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
ll sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const int maxn = 100005;
int n, ans;
struct edge
{
int to, next, w;
} e[maxn * 2];
int h[maxn], tot, rt, fa[maxn], p[maxn], siz[maxn], vis[maxn];
ll dis[maxn], d[maxn];
inline void add(int w, int u, int v)
{
e[++tot] = (edge) {v, h[u], w}; h[u] = tot;
e[++tot] = (edge) {u, h[v], w}; h[v] = tot;
}
void dfs(int u, int fa)
{
d[u] = max(d[u], dis[u]);
if (dis[u] > dis[rt]) rt = u;
for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
if (v != fa) dis[v] = dis[u] + e[i].w, dfs(v, u);
}
int find(int x)
{
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
x = find(x); y = find(y);
if (x == y) return ;
if (siz[y] > siz[x]) swap(x, y);
siz[x] += siz[y]; ans = max(ans, siz[x]); fa[y] = x;
}
int main()
{
n = gi(); ++n; --n;
for (int i = 1; i < n; ++i) add(gi(), gi(), gi());
dfs(1, 0);
int tmp = rt; dis[rt] = 0; rt = 0;
dfs(tmp, 0);
dis[rt] = 0; dfs(rt, 0);
for (int i = 1; i <= n; ++i) p[i] = i;
sort(p + 1, p + n + 1, [](const int &a, const int &b) {return d[a] < d[b];});
int q = gi();
ll lim;
while (q--) {
lim = gl();
for (int i = 1; i <= n; ++i) fa[i] = i, siz[i] = 1, vis[i] = 0;
ans = 1;
for (int k = n, j = n; k >= 1; --k) {
while (d[p[j]] > d[p[k]] + lim) {
--siz[find(p[j])];
--j;
}
vis[p[k]] = 1;
for (int i = h[p[k]], v; v = e[i].to, i; i = e[i].next)
if (vis[v]) merge(p[k], v);
}
printf("%d\n", ans);
}
return 0;
}