Loading

P10037 「FAOI-R2」梨花开 (C) 题解

比较有意思的 dp 题。

思路

考虑答案至少为 \(p\) 的条件是什么。

  1. 我们显然会把根节点的热量平均分配到深度前 \(p\) 小的点上,那么要求 \(\min_{j=1}^n \sum_{i=1}^j v_i+ \frac{k}{p}\ge 0\)
  2. 其次,在热量到达前 \(k\) 小的点前,\(\sum_{i=1} v_i\) 不可以小于 \(0\)

只要满足了这两个条件,我们的答案就至少为 \(p\),假设这样的方案数为 \(f_p\)

那么答案为:

\[\sum f_p \]

如何求解。

\(dp_{i,j}\) 为第 \(i\) 步后 \(\sum_{k=1}^{i}v_k=j\) 的方案数。

在转移时可以判断一下是否合法即可。

转移可以用前缀和优化。

复杂度:\(O(nmt^2)\)

可以获得 \(20\) 分。

发现我们的限制只和 \(dep_i\)\(\frac{k}{p}\) 有关。

因为操作只有 \(t\) 次,所以要求 \(dep_i\le t\)

而根据整除分块,\(\frac{k}{p}\) 只有根号项,所以记忆化一下,可以优化复杂度。

复杂度:\(O((t+\sqrt n)mt^2)\)

视常数可以获得 \(50\sim 60\) 分。

容易发现我们满足前缀和不小于 \(0\) 的 dp 都是一样的操作。

那么我们可以进行预处理。

\(f_{i,j}\) 表示在 \(i\) 轮后满足前缀和一直不小于 \(0\),且 \(\sum_{k=1}^i v_k=j\) 的方案数。

转移同样枚举下一位然后前缀和优化。

同样,我们想要仿造的求出 \(g_{i,j}\) 表示 \(i\) 轮后满足前缀和一直不小于 \(j\) 的方案数。

这是我们直接 dp 的后半段。

这个怎么求呢?

这类问题好像有一个比较常见的 trick。

就是在维护前缀和的最小值时,我们正着不好做,但是反过来是很好求的。

考虑反过来怎么求前缀和的最小值:

\[sum\rightarrow min(0,sum+v) \]

这个式子我们就可以 dp 了。

求出了 \(g_{i,j}\) 以后,就可以轻松搭配 \(f_{i,j}\) 求出这个询问的答案了。

时间复杂度:\(O((\sqrt k+t)mt+mt^2)\)

Code

#include <bits/stdc++.h>
using namespace std;

#define eb(...) emplace_back(__VA_ARGS__)
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)

const int N = 1e6 + 10;
const int mod = 998244353;

int n, m, t, k, ans, d[N], R[N], sm[N];
int dp1[510][30000];
int dp2[510][30000];
vector<int> to[N];

inline void dfs(int x, int f) {
  d[x] = d[f] + 1;
  for (auto i : to[x])
    if (i != f) dfs(i, x);
}
inline void add(int&x, int y) {
  x = (x + y >= mod ? x + y - mod : x + y);
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> t >> k;
  fro(i, 1, n - 1) {
    int u, v;
    cin >> u >> v;
    to[u].eb(v);
    to[v].eb(u);
  }
  dfs(1, 0);
  sort(d + 1, d + n + 1);
  dp1[0][R[0] = 0] = dp2[0][R[0] = 0] = 1;
  fro(i, 1, t) {
    fro(j, 0, R[i - 1]) {
      add(dp1[i][max(0, j - m)], dp1[i - 1][j]);
      add(dp1[i][j + m + 1], mod - dp1[i - 1][j]);
      add(dp2[i][max(0, j - m)], dp2[i - 1][j]);
      add(dp2[i][j + m + 1], mod - dp2[i - 1][j]);
    }
    R[i] = R[i - 1] + m;
    fro(j, 1, R[i] + 1) add(dp1[i][j], dp1[i][j - 1]);
    fro(j, 1, R[i] + 1) add(dp2[i][j], dp2[i][j - 1]);
    fro(j, 0, R[i - 1]) if (m - j > 0) add(dp2[i][0], 1ll * (m - j) * dp2[i - 1][j] % mod);
  }
  fro(i, 1, t) fro(j, 1, R[i]) add(dp2[i][j], dp2[i][j - 1]);
  fro(i, 1, n) {
    int v = k / i, p = min(t + 2, d[i]);
    if (i == 1) {
      sm[i] = dp2[t][min(v, R[t])];
    } else if (v != k / (i - 1) || p != min(t + 2, d[i - 1])) {
      int l = p - 2;
      int r = t - p + 2;
      fro(j, 0, R[l]) add(sm[i], 1ll * dp1[l][j] * dp2[r][min(v + j, R[r])] % mod);
    } else sm[i] = sm[i - 1];
    add(ans, sm[i]);
  }
  cout << ans << "\n";
  return 0;
}
posted @ 2024-06-25 16:49  JiaY19  阅读(3)  评论(0编辑  收藏  举报