1. 程式人生 > 實用技巧 >[CF516D] Drazil and Morning Exercise 題解

[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;
}