diff --git a/TangDou/Topic/HuanGenDp/P1364_HuanGenDp.cpp b/TangDou/Topic/HuanGenDp/P1364_HuanGenDp.cpp new file mode 100644 index 0000000..182d689 --- /dev/null +++ b/TangDou/Topic/HuanGenDp/P1364_HuanGenDp.cpp @@ -0,0 +1,40 @@ +#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]) { + if (v == fa) continue; + dfs1(v, u, dep + 1); + siz[u] += siz[v]; + } + dp[1] += dep * a[u]; // 先算出1点的总代价 +} +void dfs2(int u, int fa) { + for (auto v : g[u]) { + if (v == fa) continue; + dp[v] = dp[u] + siz[1] - siz[v] * 2; + dfs2(v, u); + } + ans = min(ans, dp[u]); +} +int main() { + 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); + } + dfs1(1, 0, 0); + dfs2(1, 0); // 换根 + cout << ans; + return 0; +} diff --git a/TangDou/Topic/【换根】dfs专题.md b/TangDou/Topic/【换根】dfs专题.md index 1df0257..5d9dba1 100644 --- a/TangDou/Topic/【换根】dfs专题.md +++ b/TangDou/Topic/【换根】dfs专题.md @@ -343,7 +343,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换根直接转换 #### [$P2986$ $Great$ $Cow$ $Gathering$ $G$](https://www.luogu.com.cn/problem/P2986)