[CF516D] Drazil and Morning Exercise 題解
阿新 • • 發佈:2020-11-22
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; }