Codeforces - 500D. New Year Santa Network - 树形dp、数学、思维

New Year Santa Network

题目链接

分类:树形dp、思维、数学

1.题意概述

  • 给你由n个节点构成的树,有(n-1)条边,随机选取其中三个节点a、b、c,定义 dist=dis(a,b)+dis(b,c)+dis(c,a) ,现在有q次操作,每次选取某条边,使得它的边权由原先的 wi 变成 li ,问你每次操作以后的期望 E(dist) 是多少?

2.解题思路

  • 根据期望性质 E[dis(a,b)+dis(b,c)+dis(c,a)]=E[dis(a,b)]+E[dis(b,c)]+E[dis(c,a)]
    • 方法一:按边考虑,n个点中任意取3个点的取法是 C3n 种,我们考虑每条边出现的概率。如果我们选定边 ab ,那么第三个点 c 就在剩下的(n2)个点中取,也就是边 ab 会出现 (n2) 次,假设这条边下面的儿子个数为 son[x] ,而经过这条边的种类数 son[x](nson[x]) ,所以最终概率为 pab=son[x](nson[x])(n2)C3n ,而总的期望就是 E(dist)=E[dis(i,j)]=pijwij !而统计儿子个数我们可以通过树形dp跑一遍dfs就可以求得,而每条边的权值的变动,只会影响 wij ,根据期望性质,我们可以先求得边尚未改变的期望,然后减去 (lijwij)pij 就是结果!
    • 方法二:按点考虑,画图容易发现,树中任意选三点构成合法路线,某条边一定会经过两次,那么我们对于这条边两侧的点考虑,假设左侧有 a 个点,右侧有b个点,那么这三个点的选法有 C2aC1b+C1aC2b 种,结果就是 2(C2aC1b+C1aC2b)C3n ,下面做法和方法一类似。

3.AC代码

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define maxn 100010
#define lson root << 1
#define rson root << 1 | 1
#define lent (t[root].r - t[root].l + 1)
#define lenl (t[lson].r - t[lson].l + 1)
#define lenr (t[rson].r - t[rson].l + 1)
#define N 1111
#define eps 1e-6
#define pi acos(-1.0)
#define e exp(1.0)
#define Close() ios::sync_with_stdio(0),cin.tie(0)
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
typedef unsigned long long ull;
struct node
{
    int to, id;
    node(int a, int b) { to = a; id = b; }
};
vector<node> g[maxn];
double p[maxn];
int son[maxn], w[maxn];
void dfs(int u, int fa, int pid, const int &n)
{
    son[u] = 1;
    int sz = g[u].size();
    for (int i = 0; i < sz; i++)
    {
        int v = g[u][i].to;
        int id = g[u][i].id;
        if (v != fa)
        {
            dfs(v, u, id, n);
            son[u] += son[v];
        }
    }
    p[pid] = 6.0 * son[u] * (n - son[u]) / n / (n - 1);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long _begin_time = clock();
#endif
    int n, m;
    scanf("%d", &n);
    for (int i = 1; i < n; i++)
    {
        int a, b;
        scanf("%d%d%d", &a, &b, &w[i]);
        g[a].push_back(node(b, i));
        g[b].push_back(node(a, i));
    }
    dfs(1, -1, 0, n);
    double ans = 0;
    for (int i = 1; i < n; i++)
        ans += w[i] * p[i];
    scanf("%d", &m);
    while (m--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        int delta = w[a] - b;
        ans -= delta * p[a];
        printf("%.12f\n", ans);
        w[a] -= delta;
    }
#ifndef ONLINE_JUDGE
    long _end_time = clock();
    printf("time = %ld ms.", _end_time - _begin_time);
#endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值