有一类树上背包(例如 CF1097G Vladislav and a Great Legend),第二维表示子树内选的点数且限制第二维不超过 m m m,其总复杂度可证明为 O ( n m ) O(nm) O(nm)。
它们的代码一般长这样:
void dfs(int u,int last) {
sz[u]=1;
// initialize dp[u]
for(int k=head[u];k;k=e[k].next)
{
int v=e[k].to; if(v==last) continue;
dfs(v,u);
for(int j=0;j<=min(m,sz[u]);++j)
for(int k=0;k<=min(m-j,sz[v]);++k)
// dp[u][j] * dp[v][k] -> dp[u][j+k]
}
}
为了避免混淆,用 s z sz sz表示已经每个点已经合并上来过的点数, s i z e size size表示每个点的子树大小。
主要的时间开销是在合并 u u u和 v v v两个点的背包上。分四种情况来讨论:
- s z [ u ] ≥ m , s i z e [ v ] ≥ m sz[u] \ge m, size[v] \ge m sz[u]≥m,size[v]≥m
一次合并的复杂度为 O ( m 2 ) O(m^2) O(