diff --git a/TangDou/Topic/HuanGenDp/P1364_HuanGenDp.cpp b/TangDou/Topic/HuanGenDp/P1364_HuanGenDp.cpp index 182d689..c26cea5 100644 --- a/TangDou/Topic/HuanGenDp/P1364_HuanGenDp.cpp +++ b/TangDou/Topic/HuanGenDp/P1364_HuanGenDp.cpp @@ -1,40 +1,60 @@ #include using namespace std; -#define LL long long -const int N = 1e5 + 10; -vector g[N]; -int a[N]; -int dp[N], siz[N]; -int ans = 1e9; -void dfs1(int u, int fa, int dep) { - siz[u] = a[u]; - for (auto v : g[u]) { +const int N = 1e5 + 10, M = N << 1; +const int INF = 0x3f3f3f3f; + +// 链式前向星 +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++; +} +int c[N]; +int f[N], sz[N]; +int ans = INF; + +// 第一次dfs,获取在以1为根的树中: +// 1、每个节点分别有多少个子节点,填充sz[]数组 +// 2、获取到f[1],f[1]表示在1点设置医院的代价 +// 获取到上面这一组+一个数据,才能进行dfs2进行换根 +void dfs1(int u, int fa, int step) { + sz[u] = c[u]; + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; if (v == fa) continue; - dfs1(v, u, dep + 1); - siz[u] += siz[v]; + dfs1(v, u, step + 1); + sz[u] += sz[v]; } - dp[1] += dep * a[u]; // 先算出1点的总代价 + f[1] += step * c[u]; // 先算出1点的总代价 } + void dfs2(int u, int fa) { - for (auto v : g[u]) { + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; if (v == fa) continue; - dp[v] = dp[u] + siz[1] - siz[v] * 2; + f[v] = f[u] + sz[1] - sz[v] * 2; dfs2(v, u); } - ans = min(ans, dp[u]); + ans = min(ans, f[u]); } + int main() { + // 初始化链式前向星 + memset(h, -1, sizeof h); + int n; cin >> n; for (int i = 1; i <= n; i++) { - cin >> a[i]; - int u, v; - cin >> u >> v; - if (u > 0) g[i].push_back(u), g[u].push_back(i); - if (v > 0) g[i].push_back(v), g[v].push_back(i); + cin >> c[i]; + int a, b; + cin >> a >> b; + if (a) add(a, i), add(i, a); // 是一个二叉树结构,与左右节点相链接,但有可能不存在左或右节点,不存在时,a或b为0 + if (b) add(b, i), add(i, b); } + // 1、准备运作 dfs1(1, 0, 0); - dfs2(1, 0); // 换根 - cout << ans; + // 2、换根dp + dfs2(1, 0); + // 输出答案 + cout << ans << endl; return 0; } diff --git a/TangDou/Topic/【换根】dfs专题.md b/TangDou/Topic/【换根】dfs专题.md index 5d9dba1..7e1ef07 100644 --- a/TangDou/Topic/【换根】dfs专题.md +++ b/TangDou/Topic/【换根】dfs专题.md @@ -344,14 +344,15 @@ int main() { **三、$O(N)$算法** 如果$n=1e6$,那么就要考虑换根$dp$了 -我们考虑相邻的医院是否存在转换关系 -设其中一个医院为u(父节点),另一个为v(子节点) -如果把u点的医院改为v点,则发现: -以v为根的子树的集合的所有人少走1步,但是另一集合的所有人要多走一步 -设siz[i]表示以i为根节点的集合人的总数,dp[i]表示在i点设置医院的代价,则可转换成: -dp[v]=dp[u]+(siz[1]-siz[v])-siz[v]:其中siz[1]表示全部人的数量 -思路: -1.先算出1个点的代价,之后dp换根直接转换 +我们考虑相邻的医院是否存在转换关系,设其中一个医院为$u$(父节点),另一个为$v$(子节点) +如果把$u$点的医院改为$v$点,则发现: +以$v$为根的子树的集合的所有人少走$1$步,但是另一集合的所有人要多走一步 +设$sz[i]$表示以$i$为根节点的集合人的总数,$f[i]$表示在$i$点设置医院的代价,则可转换成: +$$\large f[v]=f[u]+(sz[1]-sz[v])-sz[v]=f[u]+sz[1]-2\times sz[v]$$ +> 其中$sz[1]$表示全部人的数量,一般也写做$n$ + +**思路**: +先算出$1$个点的代价,之后$dp$换根直接转换 #### [$P2986$ $Great$ $Cow$ $Gathering$ $G$](https://www.luogu.com.cn/problem/P2986)