树形背包优化 (updating)

    科技2025-12-28  9

    自从上次模拟赛后,我决定隔一段时间整理一些背包优化,可能毫无顺序

    1. d f s dfs dfs的时候保存链的信息

    一道模拟赛题:给定一棵树,有 n n n各节点,每个节点都有一个非负权值,对于每条边,你有 0.5 0.5 0.5的概率断掉这条边,求以 1 1 1为根遍历的数的权值之和为 k k k的概率(答案对 998244353 998244353 998244353取模) 1 < = n , k < = 5000 1 <= n,k <= 5000 1<=n,k<=5000 直接设 d p [ i ] [ j ] dp[i][j] dp[i][j]代表以 i i i为根的子树权值之和为 k k k的概率,这样 d p dp dp的复杂度是 O ( n 3 ) O(n^3) O(n3) 那么怎么优化呢,我们只需设 d p [ i ] [ j ] dp[i][j] dp[i][j]为遍历完以 i i i为根的子树,权值之和为 k k k的概率 注意,这两个状态的不同是第二个状态也记录了从 1 1 1走到 i i i的情况(因为遍历是从 1 1 1开始的) 具体转移看代码即可

    C o d e Code Code

    #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> using namespace std; #define int long long const int MAXN = 5000, P = 499122177, Mod = 998244353; struct w{ int to, nx; }head[MAXN + MAXN + 10]; int a[MAXN + 10], val[MAXN + 10], size[MAXN + 10], dp[MAXN + 10][MAXN+ 10], n, k; inline void add(int x, int y, int i){head[i].to = y, head[i].nx = a[x]; a[x] = i;} inline int read(){ int x = 0; char c = getchar(); while (!isdigit(c))c = getchar(); while (isdigit(c))x = (x << 1) + (x << 3) + (c & 15), c = getchar(); return x; } void dfs(int x, int fa){ size[x] = 1; for (register int i = a[x]; i; i = head[i].nx){ int v = head[i].to; if (v == fa) continue; for (register int j = val[x]; j <= k; ++j) dp[v][val[v] + j] = dp[x][j] * P % Mod; dfs(v, x); size[x] += size[v]; for (register int j = val[x]; j <= k; ++j) dp[x][j] = (dp[x][j] * P + dp[v][j]) % Mod; } } signed main(){ freopen ("luge.in", "r", stdin); freopen ("luge.out", "w", stdout); n = read(), k = read(); for (register int i = 1; i <= n; ++i) val[i] = read(); for (register int i = 1; i < n; ++i){ int x = read(), y = read(); add(x, y, i + i - 1); add(y, x, i + i); } dp[1][val[1]] = 1; dfs(1, 0); cout << dp[1][k] << endl; return 0; }
    Processed: 0.015, SQL: 9