2022校賽 - G 食堂在哪裡
阿新 • • 發佈:2022-05-23
2022校賽 - G 食堂在哪裡
換根dp
首先樹dp
- \(f[u]\) :以 \(u\) 為根的子樹中的學生,到 \(u\) 這個點的距離和
- \(g[u]\) :以 \(u\) 為根的子樹中的學生,包括 \(u\) 的學生,吃完 \(u\) 的麵包後還需要向上走的人數
轉移:
\(f[u]=\sum f[v]+g[v]\)
\(g[u]=max(a[u]+\sum g[v]-b[u],0)\)
然後換根dp
- \(ansf[u]\) :以 \(u\) 結點為食堂,所有學生到這個點的距離
- \(ansg[u]\) :以 \(u\) 結點為食堂,所有學生到這個點,還還沒吃飽的人數(可視為要去食堂吃飯的人)
\(ansf[v]=f[v]+ansf[u]-f[v]-g[v]+max(ansg[u]-g[v],0)\)
\(f[v]\) :\(v\) 子樹的到 \(v\) 的距離
\(ansf[u]-f[v]-g[v]\) :除了 \(v\) 子樹,別的地方到 \(u\) 的距離
\(max(ansg[u]-g[v],0)\) :除了 \(v\) 子樹的人,別的地方的人到了 \(u\), 但還沒吃麵包還需要向 \(v\) 結點走的人數
\(ansg[v]=max(ansg[u]-g[v],0)+g[v]\)
\(max(ansg[u]-g[v],0)\) :除了 \(v\) 子樹的人,別的地方的人到了 \(u\)
\(g[v]\) :\(v\) 子樹的人走到 \(v\) 結點還沒吃到麵包的人數
#include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <cmath> using namespace std; typedef long long ll; const int N = 1e5 + 10; int n; ll a[N], b[N]; ll f[N], g[N], ansf[N], ansg[N]; vector<int> G[N]; void add(int a, int b) { G[a].push_back(b); } void dfs(int u, int fa) { f[u] = 0, g[u] = a[u]; for (auto v : G[u]) { if (v == fa) continue; dfs(v, u); f[u] += f[v] + g[v]; g[u] += g[v]; } g[u] = max(0ll, g[u] - b[u]); } void dfs2(int u, int fa) { for (auto v : G[u]) { if (v == fa) continue; ansf[v] = f[v] + ansf[u] - f[v] - g[v] + max(0ll, ansg[u] - g[v]); ansg[v] = max(0ll, ansg[u] - g[v]) + g[v]; dfs2(v, u); } } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) cin >> b[i]; for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; add(u, v), add(v, u); } dfs(1, -1); ansf[1] = f[1], ansg[1] = g[1]; dfs2(1, -1); ll minn = *min_element(ansf + 1, ansf + n + 1); cout << minn << endl; for (int i = 1; i <= n; i++) if (ansf[i] == minn) cout << i << " "; cout << endl; return 0; }