Codeforces 1606F. Tree Queries
阿新 • • 發佈:2021-11-19
Codeforces 1606F. Tree Queries
對於詢問 \(v_j,\,k_j\) , 發現答案一定在 \(\left[\,1,\,\dfrac{n}{k_j}\,\right]\) 內. 注意到這個 \(\dfrac{n}{k}\) , 可以考慮根號分治.
- \(k \le \sqrt{n}\) 可以直接樹上揹包預處理出答案陣列 \(f_{v,\,k}\) . 轉移時考慮每個兒子是否刪掉, 轉移方程為 \(f_{u,\,j}=\max\{f_{u,\,j}+1,\,f_{u,\,j}+f_{v,\,j}-j\}\) . 該部分的時空複雜度均為 \(\mathcal O(n\sqrt n)\)
- \(k>\sqrt{n}\) 此時一定有 \(m\le \sqrt{n}\) . 於是我們可以預處理出 \(g_{u,\,k}\) 表示對於節點 \(u\) , 其子樹內刪除 \(k\) 個點之後的最大兒子數. 轉移時按照樹形揹包的方式, 考慮每個兒子是否刪掉, 狀態轉移方程為 \(g'_{u,\,j}=\max\limits_{j_1+j_2=j}\{[\,j_1\le \operatorname{siz}_u\and j_2\le \operatorname{siz}_v\,]\,(g_{u,\,j_1}+\max\{1,\,g_{v,\,j_2-1}\})\}\) . 由於樹形揹包的時間複雜度為 \(\mathcal O(nk)\)
於是總時間複雜度為 \(\mathcal O(n\sqrt n)\) . 可以通過.
注意到如果開兩個陣列 \(f,g\) 會爆空間, 於是重複利用即可.
參考程式碼
#include <bits/stdc++.h> using namespace std; template<typename _Tp> _Tp &min_eq(_Tp &x, const _Tp &y) { return x = min(x, y); } template<typename _Tp> _Tp &max_eq(_Tp &x, const _Tp &y) { return x = max(x, y); } static constexpr int inf = 0x3f3f3f3f; static constexpr int Maxn = 2e5 + 5; static constexpr int BLOCK = 500; int n, q, blk; vector<int> g[Maxn]; int qv[Maxn], qk[Maxn]; int ans[Maxn]; int f[Maxn][BLOCK]; void dfs1(int u, int fa) { for (int j = 0; j <= blk; ++j) f[u][j] = 0; for (const int &v: g[u]) if (v != fa) { dfs1(v, u); for (int j = 0; j <= blk; ++j) { f[u][j] = max(f[u][j] + 1, f[u][j] + f[v][j] - j); } } } // dfs1 int sz[Maxn]; void dfs2(int u, int fa) { for (int i = 0; i <= blk; ++i) f[u][i] = -inf; f[u][0] = 0; sz[u] = 1; for (const int &v: g[u]) if (v != fa) { dfs2(v, u); static int g[BLOCK]; for (int i = 0; i <= sz[u] + sz[v] && i <= blk; ++i) g[i] = -inf; for (int i = 0; i <= sz[u] && i <= blk; ++i) for (int j = 0; j <= sz[v] && i + j <= blk; ++j) { max_eq(g[i + j], f[u][i] + 1); max_eq(g[i + j + 1], f[u][i] + f[v][j]); } for (int i = 0; i <= sz[u] + sz[v] && i <= blk; ++i) f[u][i] = g[i]; sz[u] += sz[v]; } } // dfs2 int main(void) { scanf("%d", &n); blk = (int)sqrt(n); for (int i = 1; i <= n - 1; ++i) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } scanf("%d", &q); for (int i = 1; i <= q; ++i) scanf("%d%d", &qv[i], &qk[i]); memset(ans, 0, sizeof(ans)); dfs1(1, 0); for (int i = 1; i <= q; ++i) if (qk[i] <= blk) ans[i] = f[qv[i]][qk[i]]; dfs2(1, 0); for (int i = 1; i <= q; ++i) if (qk[i] > blk) { for (int j = 0; j <= blk; ++j) { max_eq(ans[i], f[qv[i]][j] - qk[i] * j); } } for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]); exit(EXIT_SUCCESS); } // main