[2020CCPC威海C] Rencontre - 結論,樹形dp
阿新 • • 發佈:2020-10-31
Description
給定一棵樹,有三個人,每個人有一個點集,表示這個人會等概率隨機出現在這些點上。對於每一種確定的情況,三人會選擇一個點使得到他們的總距離和最小。求距離和的期望。
Solution
首先由結論:距離和為 \(\frac 1 2 d(a,b) + d(a,c) + d(b,c)\)。
根據期望的性質,我們可以將每個部分拆出來分別統計。下面考慮如何統計 \(d(a,b)\)。
我們可以分別統計每條邊的貢獻,這樣只需要一個基礎的樹形 dp 就可以了。
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 1000005; vector<pair<int, int>> g[N]; int n; struct Tree { int val[2][N], sum[2][N], vis[N], ans; Tree() { memset(val, 0, sizeof val); memset(sum, 0, sizeof sum); memset(vis, 0, sizeof vis); ans = 0; } void tag(int pos, int col) { val[col][pos]++; } void dfs(int p) { vis[p] = 1; for (int k = 0; k < 2; k++) sum[k][p] = val[k][p]; for (auto pr : g[p]) { int q = pr.first, w = pr.second; if (!vis[q]) { dfs(q); for (int k = 0; k < 2; k++) { sum[k][p] += sum[k][q]; ans += sum[k][q] * (sum[k ^ 1][0] - sum[k ^ 1][q]) * w; } } } } int solve() { for (int i = 1; i <= n; i++) { for (int k = 0; k < 2; k++) sum[k][0] += val[k][i]; } dfs(1); return ans; } } ab, ac, bc; int na, nb, nc, a[N], b[N], c[N], t1, t2, t3; signed main() { ios::sync_with_stdio(false); cin >> n; for (int i = 1; i < n; i++) { cin >> t1 >> t2 >> t3; g[t1].push_back({t2, t3}); g[t2].push_back({t1, t3}); } cin >> na; for (int i = 1; i <= na; i++) cin >> a[i], ab.tag(a[i], 0), ac.tag(a[i], 0); cin >> nb; for (int i = 1; i <= nb; i++) cin >> b[i], ab.tag(b[i], 1), bc.tag(b[i], 0); cin >> nc; for (int i = 1; i <= nc; i++) cin >> c[i], ac.tag(c[i], 1), bc.tag(c[i], 1); int sumDisAB = ab.solve(); int sumDisAC = ac.solve(); int sumDisBC = bc.solve(); double ans = 0; ans += sumDisAB * 1.0 / na / nb; ans += sumDisAC * 1.0 / na / nc; ans += sumDisBC * 1.0 / nb / nc; ans /= 2; printf("%.10lf\n",ans); return 0; }