From bb84f7fd9f756efa6002bc09acd0b4d267a42bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 16 Jan 2024 14:48:15 +0800 Subject: [PATCH] 'commit' --- TangDou/Topic/HuanGenDp/P6419.cpp | 138 ++++++++---------- ...dfs专题.md => 【换根DP】专题.md} | 44 +++++- 2 files changed, 104 insertions(+), 78 deletions(-) rename TangDou/Topic/{【换根】dfs专题.md => 【换根DP】专题.md} (93%) diff --git a/TangDou/Topic/HuanGenDp/P6419.cpp b/TangDou/Topic/HuanGenDp/P6419.cpp index 8b07f11..943129c 100644 --- a/TangDou/Topic/HuanGenDp/P6419.cpp +++ b/TangDou/Topic/HuanGenDp/P6419.cpp @@ -1,85 +1,75 @@ #include -#define ll long long using namespace std; -inline ll read() { - char c = getchar(); - ll a = 0, b = 1; - for (; c < '0' || c > '9'; c = getchar()) - if (c == '-') b = -1; - for (; c >= '0' && c <= '9'; c = getchar()) a = a * 10 + c - 48; - return a * b; +const int N = 500010, M = N << 1; +#define int long long + +int n, K; + +// 链式前向星 +int e[M], h[N], idx, w[M], ne[M]; +void add(int a, int b, int c = 0) { + e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } -struct edge { - ll next, to, v; -} e[1000501]; -ll head[1000501], tot, n, k, vis[1000501], f[1000001], ans[1000001], sum[1000001]; -ll len[1000501], slen[1000501], dis[1000501], sdis[1000501]; // ·Ö±ðÊÇÀëi×îÔ¶µÄµãºÍ´ÎÔ¶µÄµãµÄ¾àÀ룬ÒÔ¼°ÕâÌõÁ´µÄµÚÒ»¸öµãÔÚÄÄÀï -ll up[1000501]; -inline void add(ll i, ll j, ll k) { - e[++tot].next = head[i]; - e[tot].to = j; - e[tot].v = k; - head[i] = tot; -} -void dfsfirst(ll x, ll fa) { - if (vis[x]) sum[x]++; - for (ll i = head[x]; i != 0; i = e[i].next) { - ll u = e[i].to; - if (u == fa) continue; - dfsfirst(u, x); - if (sum[u]) { - ll now = len[u] + e[i].v; - f[x] += f[u] + e[i].v * 2; - if (len[x] < now) - sdis[x] = dis[x], - dis[x] = u, - slen[x] = len[x], - len[x] = now; - else if (slen[x] < now) - sdis[x] = u, - slen[x] = now; + +int pos[N]; +int sz[N], g[N], f[N]; +int len[N], id[N], slen[N]; +void dfs1(int u, int fa) { + if (pos[u]) sz[u] = 1; + for (int i = Head[u]; i; i = Edge[i].next) { + int v = Edge[i].to, w = Edge[i].val; + if (v == fa) continue; + dfs1(v, u); + if (sz[v]) { + g[u] += g[v] + 2 * w; + int now = len[v] + w; + if (now >= len[u]) + slen[u] = len[u], len[u] = now, id[u] = v; + else if (now > slen[u]) + slen[u] = now; } - sum[x] += sum[u]; + sz[u] += sz[v]; } } -void dfsecond(ll x, ll fa) // ¸üÐÂÈ«¾Ö´ð°¸ -{ - for (ll i = head[x]; i != 0; i = e[i].next) { - ll u = e[i].to; - if (u == fa) continue; - if (sum[u] == 0) { - ans[u] = ans[x] + e[i].v * 2; - up[u] = max(up[x], len[x]) + e[i].v; - } else if (sum[u] == k) { - ans[u] = f[u]; - up[u] = 0; - } else { - ans[u] = ans[x]; - if (dis[x] == u) - up[u] = max(up[x], slen[x]) + e[i].v; // Ϊʲô´Î³¤¾Í²»»á¾­¹ý,ÒòΪÎÒÉÏÃæÃ¿Ìõ¶¼Ö»È¡ÁËÒ»Ìõ×î´óÖµ°¡¡£¡£¡£ - else - up[u] = max(up[x], len[x]) + e[i].v; - } - dfsecond(u, x); +void dfs2(int u, int fa) { + for (int i = Head[u]; i; i = Edge[i].next) { + int v = Edge[i].to, w = Edge[i].val; + if (v == fa) continue; + if (!sz[v]) + f[v] = f[u] + 2 * w, len[v] = len[u] + w; + else if (K - sz[v]) { + f[v] = f[u]; + if (id[u] != v && len[v] < len[u] + w) + slen[v] = len[v], len[v] = len[u] + w, id[v] = u; + else if (len[v] < slen[u] + w) + slen[v] = len[v], len[v] = slen[u] + w, id[v] = 1; + else if (slen[v] < len[u] + w && id[u] != v) + slen[v] = len[u] + w; + else if (slen[v] < slen[u] + w) + slen[v] = slen[u] + w; + } else + f[v] = g[v]; + dfs2(v, u); } } -int main() { - n = read(); - k = read(); - for (ll i = 1; i < n; i++) { - ll a = read(), b = read(), c = read(); - add(a, b, c); - add(b, a, c); - } - for (ll i = 1; i <= k; i++) { - vis[read()] = 1; +signed main() { + // 初始化链式前向星 + memset(h, -1, sizeof h); + + cin >> n >> K; + for (int i = 1; i < n; i++) { + int a, b, c; + cin >> a >> b >> c; + add(a, b, c), add(b, a, c); } - dfsfirst(1, 0); - ans[1] = f[1]; - up[1] = 0; - dfsecond(1, 0); - for (ll i = 1; i <= n; i++) { - cout << ans[i] - max(up[i], len[i]) << endl; + for (int i = 1; i <= K; i++) { + int x; + cin >> x; + pos[x] = 1; } - return 0; + dfs1(1, 0); + f[1] = g[1]; + + dfs2(1, 0); + for (int i = 1; i <= n; i++) cout << f[i] - len[i] << endl; } \ No newline at end of file diff --git a/TangDou/Topic/【换根】dfs专题.md b/TangDou/Topic/【换根DP】专题.md similarity index 93% rename from TangDou/Topic/【换根】dfs专题.md rename to TangDou/Topic/【换根DP】专题.md index 8468031..7299d43 100644 --- a/TangDou/Topic/【换根】dfs专题.md +++ b/TangDou/Topic/【换根DP】专题.md @@ -1,5 +1,3 @@ - - ## 换根$DP$ 换根$DP$,又叫二次扫描,是树形$DP$的一种。 @@ -853,6 +851,7 @@ $5$个点, $4$条边。当以$4$为起点时, 得到最大流量为$26$。 - ② 因为$1$号节点没有向上的路径了,所以$g[1]=f[1]$ - ③ 从$1$号根节点出发, 扫描所有节点, 自顶往下进行更新$g$数组,$g[x]$代表以$x$为根节点的整棵树的最大总流量(**不光是向下,还要有向上的**)。 + 用$du$数组存节点的度,以便判断叶子结点:第一次搜索时,状态转移是如下代码所示: - ① 如果$v$是叶子结点, $v$的子树的最大流就是 $f[u] + w[u][v]$ 这里的$w[u][v]$表示$u$ 到 $v$ 的流量,代码中用$w[i]$表示 - ② 如果$v$不是叶子结点, 流量就等于$v$的子树的最大流量和$u$到$v$的流量取最小值。 @@ -960,9 +959,46 @@ int main() { 一棵树上有一些点有人,边有通过的长度,然后对于每个点,你从这个点出发经过所有人(不用回到原来位置)的最短时间。 其它人不会动,只有你去找人。 - **思路** -https://www.luogu.com.cn/problem/solution/P6419 + +首先,第一眼,这是一道换根$dp$ + +接下来我们就要看两次$dfs$要处理什么 + + + +第一次$dfs$ + +按照换根$dp$的老套路,我们要处理子树里的信息 + +- $g[u]$:以 $u$ 为根的子树中从 $u$ 开始把所有家在这个子树内的人送回家 **并回到** $u$ **节点**的最短路程 +- $sz[u]$:家在以 $u$ 为根的子树中的人数 + + + +显然,我们可以得到 $\displaystyle g[u]=\sum_{v \in son[u]} g[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$的代码: + +