Codeforces 1606F. Tree Queries

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)\) .

于是总时间复杂度为 \(\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
posted @ 2021-11-18 22:37  cutx64  阅读(78)  评论(1)    收藏  举报