[2020CCPC威海C] Rencontre - 结论,树形dp

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;
}
上一篇:【Python初级】StringIO和BytesIO读写操作的小思考


下一篇:[Android] App在三星某些机子上闪退:"不保留活动"