main
黄海 2 years ago
parent 281abd84d2
commit bb84f7fd9f

@ -1,85 +1,75 @@
#include <bits/stdc++.h> #include <bits/stdc++.h>
#define ll long long
using namespace std; using namespace std;
inline ll read() { const int N = 500010, M = N << 1;
char c = getchar(); #define int long long
ll a = 0, b = 1;
for (; c < '0' || c > '9'; c = getchar()) int n, K;
if (c == '-') b = -1;
for (; c >= '0' && c <= '9'; c = getchar()) a = a * 10 + c - 48; // 链式前向星
return a * b; 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; int pos[N];
} e[1000501]; int sz[N], g[N], f[N];
ll head[1000501], tot, n, k, vis[1000501], f[1000001], ans[1000001], sum[1000001]; int len[N], id[N], slen[N];
ll len[1000501], slen[1000501], dis[1000501], sdis[1000501]; // ·Ö±ðÊÇÀëi×îÔ¶µÄµãºÍ´ÎÔ¶µÄµãµÄ¾àÀ룬ÒÔ¼°ÕâÌõÁ´µÄµÚÒ»¸öµãÔÚÄÄÀï void dfs1(int u, int fa) {
ll up[1000501]; if (pos[u]) sz[u] = 1;
inline void add(ll i, ll j, ll k) { for (int i = Head[u]; i; i = Edge[i].next) {
e[++tot].next = head[i]; int v = Edge[i].to, w = Edge[i].val;
e[tot].to = j; if (v == fa) continue;
e[tot].v = k; dfs1(v, u);
head[i] = tot; if (sz[v]) {
} g[u] += g[v] + 2 * w;
void dfsfirst(ll x, ll fa) { int now = len[v] + w;
if (vis[x]) sum[x]++; if (now >= len[u])
for (ll i = head[x]; i != 0; i = e[i].next) { slen[u] = len[u], len[u] = now, id[u] = v;
ll u = e[i].to; else if (now > slen[u])
if (u == fa) continue; slen[u] = now;
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;
} }
sum[x] += sum[u]; sz[u] += sz[v];
} }
} }
void dfsecond(ll x, ll fa) // ¸üÐÂÈ«¾Ö´ð°¸ void dfs2(int u, int fa) {
{ for (int i = Head[u]; i; i = Edge[i].next) {
for (ll i = head[x]; i != 0; i = e[i].next) { int v = Edge[i].to, w = Edge[i].val;
ll u = e[i].to; if (v == fa) continue;
if (u == fa) continue; if (!sz[v])
if (sum[u] == 0) { f[v] = f[u] + 2 * w, len[v] = len[u] + w;
ans[u] = ans[x] + e[i].v * 2; else if (K - sz[v]) {
up[u] = max(up[x], len[x]) + e[i].v; f[v] = f[u];
} else if (sum[u] == k) { if (id[u] != v && len[v] < len[u] + w)
ans[u] = f[u]; slen[v] = len[v], len[v] = len[u] + w, id[v] = u;
up[u] = 0; else if (len[v] < slen[u] + w)
} else { slen[v] = len[v], len[v] = slen[u] + w, id[v] = 1;
ans[u] = ans[x]; else if (slen[v] < len[u] + w && id[u] != v)
if (dis[x] == u) slen[v] = len[u] + w;
up[u] = max(up[x], slen[x]) + e[i].v; // Ϊʲô´Î³¤¾Í²»»á¾­¹ý,ÒòΪÎÒÉÏÃæÃ¿Ìõ¶¼Ö»È¡ÁËÒ»Ìõ×î´óÖµ°¡¡£¡£¡£ else if (slen[v] < slen[u] + w)
else slen[v] = slen[u] + w;
up[u] = max(up[x], len[x]) + e[i].v; } else
} f[v] = g[v];
dfsecond(u, x); dfs2(v, u);
} }
} }
int main() { signed main() {
n = read(); // 初始化链式前向星
k = read(); memset(h, -1, sizeof h);
for (ll i = 1; i < n; i++) {
ll a = read(), b = read(), c = read(); cin >> n >> K;
add(a, b, c); for (int i = 1; i < n; i++) {
add(b, a, c); int a, b, c;
} cin >> a >> b >> c;
for (ll i = 1; i <= k; i++) { add(a, b, c), add(b, a, c);
vis[read()] = 1;
} }
dfsfirst(1, 0); for (int i = 1; i <= K; i++) {
ans[1] = f[1]; int x;
up[1] = 0; cin >> x;
dfsecond(1, 0); pos[x] = 1;
for (ll i = 1; i <= n; i++) {
cout << ans[i] - max(up[i], len[i]) << endl;
} }
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;
} }

@ -1,5 +1,3 @@
## 换根$DP$ ## 换根$DP$
换根$DP$,又叫二次扫描,是树形$DP$的一种。 换根$DP$,又叫二次扫描,是树形$DP$的一种。
@ -853,6 +851,7 @@ $5$个点, $4$条边。当以$4$为起点时, 得到最大流量为$26$。
- ② 因为$1$号节点没有向上的路径了,所以$g[1]=f[1]$ - ② 因为$1$号节点没有向上的路径了,所以$g[1]=f[1]$
- ③ 从$1$号根节点出发, 扫描所有节点, 自顶往下进行更新$g$数组,$g[x]$代表以$x$为根节点的整棵树的最大总流量(**不光是向下,还要有向上的**)。 - ③ 从$1$号根节点出发, 扫描所有节点, 自顶往下进行更新$g$数组,$g[x]$代表以$x$为根节点的整棵树的最大总流量(**不光是向下,还要有向上的**)。
用$du$数组存节点的度,以便判断叶子结点:第一次搜索时,状态转移是如下代码所示: 用$du$数组存节点的度,以便判断叶子结点:第一次搜索时,状态转移是如下代码所示:
- ① 如果$v$是叶子结点, $v$的子树的最大流就是 $f[u] + w[u][v]$ 这里的$w[u][v]$表示$u$ 到 $v$ 的流量,代码中用$w[i]$表示 - ① 如果$v$是叶子结点, $v$的子树的最大流就是 $f[u] + w[u][v]$ 这里的$w[u][v]$表示$u$ 到 $v$ 的流量,代码中用$w[i]$表示
- ② 如果$v$不是叶子结点, 流量就等于$v$的子树的最大流量和$u$到$v$的流量取最小值。 - ② 如果$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$的代码:
Loading…
Cancel
Save