Loading docs/dp/tree.md +115 −4 Original line number Diff line number Diff line Loading @@ -60,12 +60,20 @@ int main() { } } ``` ### 习题 - [HDU 2196 Computer](http://acm.hdu.edu.cn/showproblem.php?pid=2196) - [POJ 1463 Strategic game](http://poj.org/problem?id=1463) - [[POI2014]FAR-FarmCraft](https://www.luogu.com.cn/problem/P3574) ## 树上背包 树上的背包问题,简单来说就是背包问题与树形 DP 的结合。 ???+note "例题[洛谷 P2014 CTSC1997 选课]" ???+note "例题[洛谷 P2014 CTSC1997 选课](https://www.luogu.com.cn/problem/P2014)" 现在有 $n$ 门课程,第 $i$ 门课程的学分为 $a_i$ ,每门课程有零门或一门先修课,有先修课的课程需要先学完其先修课,才能学习该课程。 一位学生要学习 $m$ 门课程,求其能获得的最多学分数。 Loading Loading @@ -127,12 +135,115 @@ $$ } ``` ## 习题 ### 习题 - [[CTSC1997] 选课](https://www.luogu.com.cn/problem/P2014) - [「JSOI2018」潜入行动](https://loj.ac/problem/2546) - [「SDOI2017」苹果树](https://loj.ac/problem/2268) ## 换根DP 树形 DP 中的换根 DP 问题又被称为二次扫描,通常不会指定根节点,并且根节点的变化会对一些值,例如子节点深度和、点权和等产生影响。 通常需要两次 DFS ,第一次 DFS 预处理诸如深度,点权和之类的信息,在第二次 DFS 开始运行换根动态规划。 接下来以一些例题来带大家熟悉这个内容。 ???+note "例题[[POI2008]STA-Station](https://www.luogu.com.cn/problem/P3478)" 给定一个 $n$ 个点的树,请求出一个结点,使得以这个结点为根时,所有结点的深度之和最大。 不妨令$u$为当前结点,$v$为当前节点的子节点。首先需要用$s_i$来表示以 $i$ 为根的子树中的结点个数,并且有$s_u=\sum s_v$。显然需要一次 DFS 来计算所有的$s_i$,这次的 DFS 就是预处理,我们得到了以某个结点为根时其子树中的结点总数。 考虑状态转移,这里就是体现"换根"的地方了。令$f_u$为以$u$为根时,所有结点的深度之和。 $f_v\leftarrow f_u$可以体现换根,即以$u$为根转移到以$v$为根。显然在换根的转移过程中,以$v$为根或以$u$为根会导致其子树中的结点的深度产生改变。具体表现为: - 所有在$v$的子树上的结点深度都减少了一,那么总深度和就减少了$s_v$; - 所有不在$v$的子树上的结点深度都增加了一,那么总深度和就增加了$n-s_v$; 根据这两个条件就可以推出状态转移方程$f_v = f_u - s_v + n - s_v=f_u + n - 2 \times s_v$。 于是在第二次 DFS 遍历整棵树并状态转移$f_v=f_u + n - 2 \times s_v$,那么就能求出以每个结点为根时的深度和了。最后只需要遍历一次所有根节点深度和就可以求出答案。 ??? note "参考代码" ```cppp #include <bits/stdc++.h> using namespace std; int head[1000010<<1],tot; long long n, size[1000010], dep[1000010]; long long f[1000010]; struct node { int to, next; } e[1000010<<1]; void add(int u, int v) { e[++tot] = node{v, head[u]}; head[u] = tot; } void dfs(int u, int fa) { size[u] = 1; dep[u] = dep[fa] + 1; for (int i = head[u]; i; i = e[i].next) { int v = e[i].to; if (v != fa) { dfs(v, u); size[u] += size[v]; } } } void get_ans(int u, int fa) { for (int i = head[u]; i; i = e[i].next) { int v = e[i].to; if (v != fa) { f[v] = f[u] - size[v] * 2 + n; get_ans(v, u); } } } int main() { scanf("%lld", &n); int u, v; for (int i = 1; i <= n - 1; i ++) { scanf("%d %d", &u, &v); add(u, v); add(v, u); } dfs(1, 1); for (int i = 1; i <= n; i ++) f[1] += dep[i]; get_ans(1,1); long long int ans = -1; int id; for (int i = 1; i <= n; i ++) { if (f[i] > ans) { ans = f[i]; id = i; } } printf("%d\n", id); return 0; } ``` ### 习题 - [HDU 2196 Computer](http://acm.hdu.edu.cn/showproblem.php?pid=2196) - [POJ 1463 Strategic game](http://poj.org/problem?id=1463) - [POJ 3585 Accumulation Degree](http://poj.org/problem?id=3585) - [[POI2008]STA-Station](https://www.luogu.com.cn/problem/P3478) - [[USACO10MAR]Great Cow Gathering G](https://www.luogu.com.cn/problem/P2986) - [CodeForce 708C Centroids](http://codeforces.com/problemset/problem/708/C) ## 参考资料与注释 [^note1]: [子树合并背包类型的 dp 的复杂度证明 - LYD729 的 CSDN 博客](https://blog.csdn.net/lyd_7_29/article/details/79854245) Loading
docs/dp/tree.md +115 −4 Original line number Diff line number Diff line Loading @@ -60,12 +60,20 @@ int main() { } } ``` ### 习题 - [HDU 2196 Computer](http://acm.hdu.edu.cn/showproblem.php?pid=2196) - [POJ 1463 Strategic game](http://poj.org/problem?id=1463) - [[POI2014]FAR-FarmCraft](https://www.luogu.com.cn/problem/P3574) ## 树上背包 树上的背包问题,简单来说就是背包问题与树形 DP 的结合。 ???+note "例题[洛谷 P2014 CTSC1997 选课]" ???+note "例题[洛谷 P2014 CTSC1997 选课](https://www.luogu.com.cn/problem/P2014)" 现在有 $n$ 门课程,第 $i$ 门课程的学分为 $a_i$ ,每门课程有零门或一门先修课,有先修课的课程需要先学完其先修课,才能学习该课程。 一位学生要学习 $m$ 门课程,求其能获得的最多学分数。 Loading Loading @@ -127,12 +135,115 @@ $$ } ``` ## 习题 ### 习题 - [[CTSC1997] 选课](https://www.luogu.com.cn/problem/P2014) - [「JSOI2018」潜入行动](https://loj.ac/problem/2546) - [「SDOI2017」苹果树](https://loj.ac/problem/2268) ## 换根DP 树形 DP 中的换根 DP 问题又被称为二次扫描,通常不会指定根节点,并且根节点的变化会对一些值,例如子节点深度和、点权和等产生影响。 通常需要两次 DFS ,第一次 DFS 预处理诸如深度,点权和之类的信息,在第二次 DFS 开始运行换根动态规划。 接下来以一些例题来带大家熟悉这个内容。 ???+note "例题[[POI2008]STA-Station](https://www.luogu.com.cn/problem/P3478)" 给定一个 $n$ 个点的树,请求出一个结点,使得以这个结点为根时,所有结点的深度之和最大。 不妨令$u$为当前结点,$v$为当前节点的子节点。首先需要用$s_i$来表示以 $i$ 为根的子树中的结点个数,并且有$s_u=\sum s_v$。显然需要一次 DFS 来计算所有的$s_i$,这次的 DFS 就是预处理,我们得到了以某个结点为根时其子树中的结点总数。 考虑状态转移,这里就是体现"换根"的地方了。令$f_u$为以$u$为根时,所有结点的深度之和。 $f_v\leftarrow f_u$可以体现换根,即以$u$为根转移到以$v$为根。显然在换根的转移过程中,以$v$为根或以$u$为根会导致其子树中的结点的深度产生改变。具体表现为: - 所有在$v$的子树上的结点深度都减少了一,那么总深度和就减少了$s_v$; - 所有不在$v$的子树上的结点深度都增加了一,那么总深度和就增加了$n-s_v$; 根据这两个条件就可以推出状态转移方程$f_v = f_u - s_v + n - s_v=f_u + n - 2 \times s_v$。 于是在第二次 DFS 遍历整棵树并状态转移$f_v=f_u + n - 2 \times s_v$,那么就能求出以每个结点为根时的深度和了。最后只需要遍历一次所有根节点深度和就可以求出答案。 ??? note "参考代码" ```cppp #include <bits/stdc++.h> using namespace std; int head[1000010<<1],tot; long long n, size[1000010], dep[1000010]; long long f[1000010]; struct node { int to, next; } e[1000010<<1]; void add(int u, int v) { e[++tot] = node{v, head[u]}; head[u] = tot; } void dfs(int u, int fa) { size[u] = 1; dep[u] = dep[fa] + 1; for (int i = head[u]; i; i = e[i].next) { int v = e[i].to; if (v != fa) { dfs(v, u); size[u] += size[v]; } } } void get_ans(int u, int fa) { for (int i = head[u]; i; i = e[i].next) { int v = e[i].to; if (v != fa) { f[v] = f[u] - size[v] * 2 + n; get_ans(v, u); } } } int main() { scanf("%lld", &n); int u, v; for (int i = 1; i <= n - 1; i ++) { scanf("%d %d", &u, &v); add(u, v); add(v, u); } dfs(1, 1); for (int i = 1; i <= n; i ++) f[1] += dep[i]; get_ans(1,1); long long int ans = -1; int id; for (int i = 1; i <= n; i ++) { if (f[i] > ans) { ans = f[i]; id = i; } } printf("%d\n", id); return 0; } ``` ### 习题 - [HDU 2196 Computer](http://acm.hdu.edu.cn/showproblem.php?pid=2196) - [POJ 1463 Strategic game](http://poj.org/problem?id=1463) - [POJ 3585 Accumulation Degree](http://poj.org/problem?id=3585) - [[POI2008]STA-Station](https://www.luogu.com.cn/problem/P3478) - [[USACO10MAR]Great Cow Gathering G](https://www.luogu.com.cn/problem/P2986) - [CodeForce 708C Centroids](http://codeforces.com/problemset/problem/708/C) ## 参考资料与注释 [^note1]: [子树合并背包类型的 dp 的复杂度证明 - LYD729 的 CSDN 博客](https://blog.csdn.net/lyd_7_29/article/details/79854245)