2020年CCPC威海站 C. Rencontre 树形dp求块类任意两点距离和

2020年CCPC威海站 C. Rencontre 树形dp求块类任意两点距离和

传送门:https://codeforces.ml/gym/102798/problem/C

题意

给 一 棵 树 , 树 上 的 点 分 为 三 种 。 给一棵树,树上的点分为三种。
首 先 分 别 在 三 种 点 中 随 机 选 择 一 点 a , b , c , 然 后 找 到 一 个 合 适 的 v , 使 得 首先分别在三种点中随机选择一点a,b,c,然后找到一个合适的v,使得 a,b,cv使
f = m i n ( d i s ( a , v ) + d i s ( b , v ) + d i s ( c , v ) ) f=min(dis(a,v)+dis(b,v)+dis(c,v)) f=min(dis(a,v)+dis(b,v)+dis(c,v))

求 出 f 的 期 望 。 求出f的期望。 f

思路

当 a , b , c 确 定 下 来 , 那 么 v 也 会 确 定 下 来 , 因 为 m i n , 所 以 我 们 围 绕 a , b , c 展 开 。 当a,b,c确定下来,那么v也会确定下来,因为min,所以我们围绕a,b,c展开。 a,b,cvmina,b,c

数 学 上 可 以 推 出 m i n ( d i s ( a , v ) + d i s ( b , v ) + d i s ( c , v ) ) = 1 2 ( d i s ( a , b ) + d i s ( b , c ) + d i s ( c , a ) ) 数学上可以推出min(dis(a,v)+dis(b,v)+dis(c,v))=\frac{1}{2}(dis(a,b)+dis(b,c)+dis(c,a)) min(dis(a,v)+dis(b,v)+dis(c,v))=21(dis(a,b)+dis(b,c)+dis(c,a))
这 样 我 们 就 不 需 要 考 虑 v 的 存 在 , 而 怎 么 求 d i s 呢 ? 这样我们就不需要考虑v的存在,而怎么求dis呢? vdis

显 然 d i s 中 的 点 是 在 树 上 的 , 所 以 总 贡 献 为 ∑ x ∈ a ∑ y ∈ b d i s ( x , y ) ∣ a ∣ ∣ b ∣ + ∑ x ∈ c ∑ y ∈ b d i s ( x , y ) ∣ c ∣ ∣ b ∣ + ∑ x ∈ a ∑ y ∈ c d i s ( x , y ) ∣ a ∣ ∣ c ∣ 显然dis中的点是在树上的,所以总贡献为\frac{\sum_{x\in a}\sum_{y\in b}dis(x,y)}{|a||b|}+\frac{\sum_{x\in c}\sum_{y\in b}dis(x,y)}{|c||b|}+\frac{\sum_{x\in a}\sum_{y\in c}dis(x,y)}{|a||c|} disabxaybdis(x,y)+cbxcybdis(x,y)+acxaycdis(x,y)

a , b , c 的 个 数 输 入 的 时 候 就 知 道 了 , 那 么 就 是 求 所 有 的 d i s , 即 树 上 任 意 两 点 距 离 和 。 a,b,c的个数输入的时候就知道了,那么就是求所有的dis,即树上任意两点距离和。 abcdis

很 多 人 都 知 道 , 树 上 任 意 两 点 之 间 距 离 就 是 d f s 一 遍 树 , 对 于 一 条 边 a n s + = ( n − s i z [ v ] ) ∗ s i z [ v ] ∗ w 很多人都知道,树上任意两点之间距离就是dfs一遍树,对于一条边ans+=(n-siz[v])*siz[v]*w dfsans+=(nsiz[v])siz[v]w

这 里 同 如 此 , 只 不 过 是 进 化 版 , 是 要 在 三 个 块 中 选 择 不 同 点 求 任 意 距 离 和 。 这里同如此,只不过是进化版,是要在三个块中选择不同点求任意距离和。
没 关 系 , 设 s i z [ u ] [ 1 / 2 / 3 ] 表 示 以 u 为 根 节 点 的 子 树 中 , 1 / 2 / 3 种 类 的 点 有 多 少 个 。 没关系,设siz[u][1/2/3]表示以u为根节点的子树中,1/2/3种类的点有多少个。 siz[u][1/2/3]u1/2/3

然 后 根 据 s i z 大 小 循 环 转 移 即 可 。 然后根据siz大小循环转移即可。 siz

难 点 应 该 就 是 怎 么 在 不 同 块 中 求 贡 献 了 。 首 先 d f s 1 预 处 理 出 全 部 的 s i z , 再 d f s 2 对 边 求 贡 献 。 难点应该就是怎么在不同块中求贡献了。首先dfs1预处理出全部的siz,再dfs2对边求贡献。 dfs1sizdfs2

Code

#include "bits/stdc++.h"
using namespace std;

typedef long long ll;
const int N = 1e6 + 10;

struct Edge {
    int v;
    ll w;
};

vector<Edge> g[N];
ll cnt[4], siz[N][4];;
double ans;

void dfs1(int u, int fa) {
    for(auto e : g[u]) {
        int v = e.v;
        if(v == fa) continue;
        dfs1(v, u);
        for(int i = 1;i <= 3; i++) {
            siz[u][i] += siz[v][i];
        }
    }
}

void dfs2(int u, int fa) {
    for(auto e : g[u]) {
        int v = e.v;
        if(v == fa) continue;
        dfs2(v, u);
        for(int i = 1;i <= 3; i++) {
            for(int j = 1;j <= 3; j++) {
                if(i == j) continue;
                ans += 1.0 * ((siz[1][i] - siz[v][i]) * siz[v][j] * 1.0 * e.w / (cnt[i] * cnt[j]) / 2.0);
            }
        }
    }
}

void solve() {
    int n; cin >> n;
    for(int i = 1;i < n; i++) {
        int u, v; ll w; cin >> u >> v >> w;
        g[u].push_back(Edge{v, w});
        g[v].push_back(Edge{u, w});
    }
    for(int i = 1;i <= 3; i++) {
        cin >> cnt[i];
        for(int j = 1;j <= cnt[i]; j++) {
            int x; cin >> x;
            siz[x][i]++;
        }
    }
    dfs1(1, -1);
    dfs2(1, -1);
    cout << fixed << setprecision(12) << ans << endl;
}

signed main() {
    solve();
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值