#QBXT2020 3月DP营 Day2上午

树形DP

DP上树。

存储:边表(邻接表,链式前向星)

(找节点i的子树大小)

状态设计:每个节点为状态;初始化:叶子节点为1;转移:把儿子们加起来。

顺序:从叶子节点不断向上做到根的过程。

void dfs(int now ,int fa){
    for(int p = head[now];p;p = edge[p].nxt){
        if(edge[p].nxt != fa){
            dfs(edge[p].to,now);
            f[now] += f[edge[p].to];
        }
    }
}

求树的直径

规定一棵树,输的诗经就是在树上找到两个点,使其距离最长,距离即直径。

思考可得:距离 = \(p_1 => lca(p_1,p_2) => p_2\)

枚举以每个点作为拐点的最长路径,最后对每个拐点去max。要算以i向下的最长路,以i向下的次长路,分别用f[],g[]。

初始化:f[i]、g[i] = 0

\[f[i] = max(f[j] + 1) \\ 次大路就是把当前最大路的子节点去掉之后得到的答案。 \]

求树上所有路径的总长度和

求树上路径的总长度和

\[\sum_{i = 1}^n \sum_{j = 1}^n dis(i,j) \]

g[i]所有点到根节点的距离之和,f[i]表示答案。p1到i的路径总和为:g[i] = (g[p1] + size[p1]) * (size[i] - size[p1])(size是子树的大小)。

\[f[i] = \sum_{p_j \in son(i)} f[p_j] + (g[p_j] + size[p_j]) \times (size[i] - size[p_j]) \]

或者:

\[ans = \sum_{i = 2}^n size[i] \times (n - size[i]) \times 2 \]

询问树的最大独立集

选出更多的点,使得这些点互不相邻。(没有边)

f[i][0/1]表示i这个点选/不选的方案数。

\[f[i][1] = \sum _{p_i}f[pi][0] \\ f[i][0] = \sum_{pi} max(f[p_j][0],f[p_j][1]) + 1 \]

士兵

现在要在一棵树上布置士兵,每个士兵在结点上,每个士兵可以守护其结点直接相连的全部边,问最少需要布置多少个士兵。

f[i][0/1/2],对于节点i,0表示儿子保护,1表示自己保护,2表示父亲保护。

初始化叶节点:f[i][0] = INF,f[i][1] = 1,f[i][2] = 0;

g[k][0/1]:i节点的前k个儿子是不是已经有一个儿子放了士兵

\[g[k][0] = g[k-1][0] + f[p_k][0] \\ g[k][1] = min( \\ g[k-1][0] + f[p_k][1], \\ g[k-1][1] + f[p_k][0], \\ g[h-1][0] + f[p_k][1])\\ \\ \\ f[i][0] = \\ f[i][1] = \sum_{p_j} min(f[p_j][0],f[p_j][1],f[p_j][2]) + 1;\\ f[i][2] = \sum_{p_j} f[p_j][0] \]

依赖背包问题

依赖背包问题

每个物品要选必须先选某个指定的物品问能够获得的最大价值(今明的预算方案)

把依赖关系用数连起来,f[i][j]表示i的子树用了j的体积。

初始化:f[i][0] = 0,f[i][vi] = wi,f[i][j] = -INF

转移:g[i][j]前i个儿子用掉了前j的体积,则g[0][0],g[0][j] = -INF,g[i][j] = max(g[i-1][j-k] + f[pi][k])就是前i个儿子用掉j个体积的最大价值。(逐渐跑题)

转移:f[i][0] = 0;f[i][j] = g[r][j-vi] + wi;

复杂度\(n * m^2\)

posted @ 2020-03-29 19:02  CYC的幸福生活  阅读(127)  评论(0编辑  收藏  举报