diff --git a/TangDou/Topic/HuanGenDp/P6419.cpp b/TangDou/Topic/HuanGenDp/P6419.cpp index f358e85..54055d0 100644 --- a/TangDou/Topic/HuanGenDp/P6419.cpp +++ b/TangDou/Topic/HuanGenDp/P6419.cpp @@ -9,32 +9,43 @@ void add(int a, int b, int c = 0) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } -int n, m, k; - -int sz[N]; // 以u为根节点是否有人 -int g[N]; // 从u出发把u子树上的人都送回家再回到u所需要的时间 +int n; // n个节点 +int k; // k个需要接的人 int mx1[N]; // u的子树中最长链的长度 int mx2[N]; // u的子树中次长链 -int up[N]; // 不在u的子树内,距离u最远的那个人的家到u的距离 -int id[N]; // 最长链条是哪条链 -int ans[N]; // 从u出发把所有点都送回家再回到u的结果 +int sz[N]; // 以u为根的子树中是否有人 +int id[N]; // id[u]:u的最长链条出发,第1个经过的节点是哪个节点 +int up[N]; // 向上的最长路径:不在u的子树内,而是向u的父节点方向走,距离u最远的那个人的家到u的距离 +int f[N]; // 从u出发把u子树上的人都送回家再回到u所需要的时间 +int g[N]; // 从u出发把所有点都送回家再回到u的结果 + +// f[i] -max(up[i],mx1[i]); -// ans[i] -max(up[i],mx1[i]); +/* +功能: +第一次dfs,先递归子节点v,利用子节点v提供的信息,更新父节点u的统计信息 +统计信息包括: +① +② +③ +④ +*/ void dfs1(int u, int fa) { for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; if (v == fa) continue; - dfs1(v, u); - if (sz[v] == 0) continue; - g[u] += g[v] + 2 * w[i]; - int now = mx1[v] + w[i]; - if (now >= mx1[u]) { + dfs1(v, u); // 上来先递归u的子节点v,因为v中在初始化读入时,已经初始化了sz[v]=1,所以,可以利用这个进行更新sz[u]+=sum(sz[v]) + if (sz[v] == 0) continue; // 如果v里面没有需要接的人,那么以v为根的子树不会对u为根的子树产生贡献,放过v子树 + // 如果进入到这里,说明v子树里面有需要接的人,会对u为根的子树产生贡献,需要讨论 + f[u] += f[v] + 2 * w[i]; + int x = mx1[v] + w[i]; + if (x >= mx1[u]) { mx2[u] = mx1[u]; - mx1[u] = now; + mx1[u] = x; id[u] = v; - } else if (now >= mx2[u]) - mx2[u] = now; + } else if (x >= mx2[u]) + mx2[u] = x; sz[u] += sz[v]; } } @@ -44,13 +55,13 @@ void dfs2(int u, int fa) { int v = e[i]; if (v == fa) continue; if (sz[v] == k) { - ans[v] = g[v]; + g[v] = f[v]; up[v] = 0; } else if (sz[v] == 0) { - ans[v] = ans[u] + 2 * w[i]; + g[v] = g[u] + 2 * w[i]; up[v] = max(up[u], mx1[u]) + w[i]; } else if (sz[v] && sz[v] != k) { - ans[v] = ans[u]; + g[v] = g[u]; if (id[u] == v) up[v] = max(mx2[u], up[u]) + w[i]; else @@ -79,9 +90,9 @@ signed main() { } // 第一次dfs dfs1(1, 0); - ans[1] = g[1]; + g[1] = f[1]; // 第二次dfs dfs2(1, 0); - for (int u = 1; u <= n; u++) cout << ans[u] - max(up[u], mx1[u]) << endl; + for (int u = 1; u <= n; u++) cout << g[u] - max(up[u], mx1[u]) << endl; } \ No newline at end of file diff --git a/TangDou/Topic/【换根DP】专题.md b/TangDou/Topic/【换根DP】专题.md index 13a4916..ab18cde 100644 --- a/TangDou/Topic/【换根DP】专题.md +++ b/TangDou/Topic/【换根DP】专题.md @@ -960,110 +960,8 @@ int main() { 其它人不会动,只有你去找人。 **思路** +https://zhuanlan.zhihu.com/p/572288778 -首先,第一眼,这是一道换根$dp$ - -接下来我们就要看两次$dfs$要处理什么 - - - -**第一次$dfs$** - -按照换根$dp$的老套路,我们要处理子树里的信息 - -- $f[u]$:以 $u$ 为根的子树中从 $u$ 开始把所有家在这个子树内的人送回家 **并回到** $u$ **节点**的最短路程 -- $sz[u]$:家在以 $u$ 为根的子树中的人数 - - - -显然,我们可以得到 $\displaystyle f[u]=\sum_{v \in son[u]} f[v]+2\times w_{u \rightarrow v}$​,其中$v$是 $u$的子节点,且$sz_v \neq 0$ - -其中 $w$ 为边权 - -刚做这道题的我天真地以为这就是第一次 $dfs$ 需要处理的东西,当我写完之后测样例时,发现挂掉了,所以,我们还需要处理一些东西 - -我们手算了一遍样例,**发现我们的车可以送完人了之后不返回开始点**;也就是说再我们这么算回到起始点的数值之后还要 **减去一个最长链的长度**! - -这里的 **最长链就表示离一个点最远的人的家** - -令 $len[u]$ 表示:从 $u$ 开始的最长链,$id[u]$ 为从 $u$ 开始的最长链所经过的第一个节点(也就是 $u$ 的一个子节点或者 $u$ 的父亲节点) - -令$slen[u]$表示:为从 $u$ 开始的次长链,次长链是干啥的待会再说 - -> **解释**:$s:second$ - - - -当然了,第一次$dfs$我们还是只处理子树内的最长链,次长链 - -先放第一次$dfs$的代码: - -```cpp {.line-numbers} - -void dfs1(int u, int fa) { - for (int i = h[u]; ~i; i = ne[i]) { - int v = e[i]; - if (v == fa) continue; - dfs1(v, u); // 由底向上,先递归,再更新统计信息 - - // 如果v这个节点,及它的子节点上有人,那么需要汇总统计信息到sz[u]上去 - // 如果v上就没有人,那就不用统计了 - if (sz[v] == 0) continue; - - // ① u->v,v->u一来一回,路径翻倍 2*w[i] - // ② 所有子节点都对u有贡献,所以f[u]+ - // ③ 跑完v为根的子树后,v子树的贡献要累加到u子树上,所以f[u]+=f[v]+2*w[i] - f[u] += f[v] + 2 * w[i]; - - // len[v]:v点出发的最长链长度 - int x = len[v] + w[i]; - - // 更新最长链 - if (x >= len[u]) - slen[u] = len[u], len[u] = x, id[u] = v; - else if (x > slen[u]) // 更新次长链 - slen[u] = x; - - // 记录累计人数 - sz[u] += sz[v]; - } -} -``` - -**第二次$dfs$** -第二次$dfs$我们就要处理全局的事情了 - -令 $g[u]$ 为对于整棵树从 $u$ 开始送人 **最后回到 $u$ 的最短距离** - - - -接下来分类讨论: - -(1)、当以 $v$ 为根的子树中没有人的家,即 $sz[v] =0$ 时, $g[v]=g[u]+2\times w[i]$ - -(2)、当除了以$v$ 为根的子树其他地方没有人的家,即 $m−sz[v]=0$时, $g[v]=f[v]$ - -(3)、 其他情况,$sz[v] \neq 0$ 且 $m-sz[v] \neq 0$时,$g[v]=g[u]$ - - - -更新完 $g$ 之后,考虑如何更新最长链和次长链 - -依旧分类讨论,依旧是上面三类(这里编号就代表上面的情况) - -(1)、这种情况可以发现 $len[v]=len[u]+w[i]$,很简单 - -(2)、这种情况很容易发现完全没有必要更新 - -(3)、最烦的情况来了,这种情况下我们还要分类讨论 - ① 当 $len[u]+w≥len[v]$ 且 $id[u] \neq v$ 时,说明 $u$ 的最长链可以更新 $v$ 的最长链,那么直接更新即可 - ② 当 $len[u]+w≥len[v]$ 且 $id[u] = v$ 时,说明虽然 $u$ 的最长链的长度可以更新 $v$,但是若更新了这就不是一条链了,所以也不可以来更新 - ③ 当 $slen[u]+w≥len[v]$时, 说明 $u$ 的次长链可以用来更新 $v$ 的最长链,直接更新即可 - ④ 当 $len[u]+w≥slen[v]$ 且 $id[u] \neq v$ 时,说明 $u$ 的最长链可以更新 $v$ 的最长链,那么直接更新即可 - ⑤ 当 $len[u]+w≥slen[v]$ 且$id[u]=v$ 时, 这时与$2$)同理,不可以更新 - ⑥ 当 $slen[u]+w≥slen[v]$ 说明 $u$ 的次长链可以更新 $v$ 的次长链,直接更新即可 - -到这里,第二次$dfs$就做完了 #### [$CF708C$ $Centroids$](https://www.luogu.com.cn/problem/CF708C)