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;
}