[CF516D] Drazil and Morning Exercise 题解

Description

给定一棵 \(n\) 个点的树,边有边权。

定义 \(f_x = \max_{i=1}^n \text{dist}(x,i)\)

\(q\) 次询问最大的满足 \(\max_{x \in s} f_x - \min_{x \in s} f_x \le l\) 的连通块 \(s\) 包含的点数。

\(n \le 10^5,q \le 50\)

Sol

考虑转化问题,变为选出 \(f_i\in [L,R]\),满足 \(R-L\le l\) 的所有点组成的最大连通块。

考虑使用 two pointers 维护,那么如何维护加点和删点后连通块的大小呢?

加点所产生的连通块可以使用并查集维护,但删点就比较麻烦。

仔细思考后我们可以发现一个性质,当前 \(f_i\) 最大的点总在当前树的直径端点上,而直径的端点都在叶子上,所以删去直径的端点并不会将一个连通块变为两个。

所以将 \(f_i\) 从大到小排序,双指针从前往后扫,删点加点时维护一下每个并查集的大小即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch))  {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch))  x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}
void Write(int x) {
    if(x < 0)  putchar('-'), x = -x;
    if(x == 0)  putchar('0');
    int stk[55], tp = 0;
    while(x) stk[++tp] = x % 10, x /= 10;
    for(int i = tp; i; i--)  putchar(stk[i] + '0');
}
int first[500005], nxt[500005], to[500005], w[500005], tot = 0;
void Add(int x, int y, int z) {nxt[++tot] = first[x]; first[x] = tot; to[tot] = y; w[tot] = z;}
int n, f[100005], id[100005], dis[100005], maxn, mpos, fa[100005], sz[100005], vis[100005];
int findfa(int x) {return fa[x] == x ? x : fa[x] = findfa(fa[x]);}
void dfs(int u, int fa) {
    if(dis[u] > maxn)  maxn = dis[u], mpos = u;
    for(int e = first[u]; e; e = nxt[e]) {
        int v = to[e]; if(v == fa)  continue;
        dis[v] = dis[u] + w[e]; dfs(v, u);
    }
}
void dfs2(int u, int fa) {
    for(int e = first[u]; e; e = nxt[e]) {
        int v = to[e]; if(v == fa)  continue;
        f[v] = max(f[v], dis[u] + w[e]);
        dis[v] = dis[u] + w[e];
        dfs2(v, u);
    }
}
bool cmp(int A, int B) {
    return f[A] > f[B];
}
signed main() {
    n = Read();
    for(int i = 1; i < n; i++) {
        int x = Read(), y = Read(), z = Read();
        Add(x, y, z); Add(y, x, z);
    }
    dfs(1, 0); memset(dis, 0, sizeof(dis));
    int A = mpos, B; maxn = 0; dfs(A, 0); B = mpos;
    memset(dis, 0, sizeof(dis));
    dfs2(A, 0);
    memset(dis, 0, sizeof(dis));
    dfs2(B, 0);
    for(int i = 1; i <= n; i++)  id[i] = i;
    sort(id + 1, id + n + 1, cmp);
    int q = Read();
    while(q--) {
        memset(sz, 0, sizeof(sz)); int mx = 1;
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++)  fa[i] = i, sz[i] = 1;
        int l = 1, r = 1, x = Read();
        vis[id[1]] = 1;
        while(r + 1 <= n && f[id[l]] - f[id[r + 1]] <= x) {
            int u = id[r + 1]; vis[u] = 1;
            for(int e = first[u]; e; e = nxt[e]) {
                int v = to[e];  if(!vis[v])  continue;
                int fv = findfa(v), fu = findfa(u);
                fa[fv] = fu; sz[fu] += sz[fv];
                mx = max(mx, sz[fu]);
            }
            ++r;
        }
        while(r != n) {
            while(f[id[l]] - f[id[r + 1]] > x)  vis[id[l]] = 0, --sz[findfa(id[l])], ++l;
            while(r + 1 <= n && f[id[l]] - f[id[r + 1]] <= x) {
                int u = id[r + 1]; vis[u] = 1;
                for(int e = first[u]; e; e = nxt[e]) {
                    int v = to[e]; if(!vis[v])  continue;
                    int fv = findfa(v), fu = findfa(u);
                    fa[fv] = fu; sz[fu] += sz[fv];
                    mx = max(mx, sz[fu]);
                }
                ++r;
            }
        }
        cout << mx << endl;
    }
     return 0;
}
posted @ 2020-11-22 19:03  verjun  阅读(75)  评论(0编辑  收藏  举报