Codeforces Round 872 (Div. 1) B2. LuoTianyi and the Floating Islands (Hard Version)(概率/期望)

原题链接:B2. LuoTianyi and the Floating Islands (Hard Version)


题目大意:


给定一棵 n n n 个点, n − 1 n - 1 n1 条边的树,边权为 1 1 1 。现在假设有 k k k 个人随机地分布在这些点上,并且他们要走到同一个点 x x x

定义点 x x x 是好点,当且仅当这 k k k 个人走到 x x x 的距离之和最小。

现在问你好点 x x x 个数的期望值是多少,并输出期望值在 m o d    1 0 9 + 7 \mod 10^9+7 mod109+7 下的值。

解题思路:


首先注意到,当 k k k 为奇数时,无论我们怎么选,答案一定都会是 1 1 1

假设 k = 3 k=3 k=3 时,我们有如下这两种情况:

在这里插入图片描述

(橙色点为已选的 k k k 个点,三角标记的点为好点 x x x

注意到上面这两种情况的好点 x x x 的个数均为 1 1 1

首先,由于点的个数不能被平均分配,对于我们所选的点 x x x 来说,我们的点 x x x 经过某一条边转移到另一个点 x ′ x' x 上时,对距离之和 ∑ d i s t \sum dist dist 的贡献一定会不同。

假设我们在上图的三点在一条链上进行讨论,假设我们不选择三角符号标记的点 x = 3 x=3 x=3 作为好点,而是选择 x ′ = 2 x'=2 x=2 的点作为好点。

对于点 x ′ = 2 x'=2 x=2 这个位置而言,我们此时如果往左边走,左边的点的个数为 1 1 1 个(点 1 1 1),而右边的点的个数为 2 2 2 个(点 3 , 5 3,5 3,5)两边点的个数是不均等的。

往左走时,对 d i s t dist dist 的增量为 Δ l = − 1 + 2 = 1 \Delta_l=-1+2=1 Δl=1+2=1
往右走时,对 d i s t dist dist 的增量为 Δ r = + 1 − 2 = − 1 \Delta_r=+1-2=-1 Δr=+12=1

显然 x ′ x' x 的距离之和还能继续被降低, x ′ x' x 不会是好点。下图不在链上的情况同理。

我们可以推出一个结论,当我们沿着边走的增量均相同时,我们的点 x x x 才会是好点,增量不同时,我们最后一定会走到其中一个点上,且该点四周的增量都相同,由于图是一棵树,可以证明这个点一定存在的,而 k k k 为奇数时,这个点唯一。

  • 再考虑 k k k 为偶数的情况。

注意到,只有点沿着一条边走的增量相同时才会是好点,那么显然我们对于一条边来说,如果两边的联通分量放的点的个数相同,增量 Δ \Delta Δ 一定为 0 0 0

在这里插入图片描述
(定义好边为所有点经过这一条边的增量为 0 0 0 的边,如上图为 ( 2 , 3 ) , ( 3 , 4 ) (2,3),(3,4) (2,3),(3,4) 这两条边 )

显然 k k k 为偶数时,我们可以向一条边的两端的连通分量分别放上 k 2 k \over 2 2k 个点,使得好点的个数变多。

而且还注意到由于存在增量这个东西,我们可以得出一个结论:好点 一定是连续分布的,并且 好边 的两端一定会是 好点

根据这个结论,我们可以得到:好边也一定连续成链,那么 好点的个数 = = = 好边的个数 + 1 +1 +1

那么我们可以转化为图中一共有多少条好边。

我们考虑一条边 ( u , v ) (u,v) (u,v) 相连的两个连通分量,我们往 u u u 所在的连通分量放上 k 2 k \over 2 2k 个点,往 v v v 所在的连通分量放上 k 2 k \over 2 2k 个点,那么显然这一条边 ( u , v ) (u,v) (u,v) 就能成为好边。

我们假设边上两点 u , v u,v u,v 所在连通分量大小为 s i z [ u ] siz[u] siz[u] s i z [ v ] siz[v] siz[v]

那么这种方案数有 C s i z [ u ] k 2 ⋅ C s i z [ v ] k 2 C_{siz[u]}^{k \over 2} \cdot C_{siz[v]}^{k \over 2} Csiz[u]2kCsiz[v]2k 种。

那么好边的总期望数就是 ∑ ( C s i z [ u ] k 2 ⋅ C s i z [ v ] k 2 ) C n k \sum( C_{siz[u]}^{k \over 2} \cdot C_{siz[v]}^{k \over 2}) \over C_{n}^{k} Cnk(Csiz[u]2kCsiz[v]2k) 条。

那么化简后,好点的个数就是 ∑ ( C s i z [ u ] k 2 ⋅ C s i z [ v ] k 2 ) + C n k C n k \sum( C_{siz[u]}^{k \over 2} \cdot C_{siz[v]}^{k \over 2})+C_{n}^{k} \over C_{n}^{k} Cnk(Csiz[u]2kCsiz[v]2k)+Cnk 个了。

考虑我们怎么进行枚举边 ( u , v ) (u,v) (u,v) 的操作,我们任取一个点作为根节点,然后执行一次 D F S DFS DFS 操作。

假设我们现在在点 u u u ,我们点 u u u 的子树大小为 s i z [ u ] siz[u] siz[u] ,我们的父节点为 f a u fa_u fau ,那么对于 ( f a u , u ) (fa_u,u) (fau,u) 这条边而言,其相连的两个连通分量的大小分别为 n − s i z [ u ] n-siz[u] nsiz[u] s i z [ u ] siz[u] siz[u]

那么我们对答案的贡献即 C n − s i z [ u ] k 2 ⋅ C s i z [ v ] k 2 C_{n-siz[u]}^{k \over 2} \cdot C_{siz[v]}^{k \over 2} Cnsiz[u]2kCsiz[v]2k

我们在统计所有答案后再乘上 C n k C_{n}^{k} Cnk 的逆元即可。

时间复杂度: O ( n ) O(n) O(n)

AC代码:


#include <bits/stdc++.h>
#define YES return void(cout << "Yes\n")
#define NO return void(cout << "No\n")
using namespace std;

using ui64 = unsigned long long;
using PII = pair<int, int>;
using i64 = long long;

//组合数板子
const int mod = 1e9 + 7;
struct Comb {
    const int N;
    vector<i64> fac, invfac;
    Comb(int n) : N(n), fac(n + 2), invfac(n + 2) { init(); };
    i64 qpow(i64 x, i64 p) {
        i64 res = 1 % mod; x %= mod;
        for (; p; p >>= 1, x = x * x % mod)
            if (p & 1) res = res * x % mod;
        return res;
    }
    i64 inv(i64 x) { return qpow(x, mod - 2); };
    void init() {
        fac[0] = 1;
        for (int i = 1; i <= N; ++i) fac[i] = fac[i - 1] * i % mod;
        invfac[N] = inv(fac[N]);
        for (int i = N - 1; i >= 0; --i) invfac[i] = (invfac[i + 1] * (i + 1)) % mod;
    }
    i64 C(int n, int m) {
        if (n < m || m < 0) return 0;
        return fac[n] * invfac[m] % mod * invfac[n - m] % mod;
    }
    i64 A(int n, int m) {
        if (n < m || m < 0) return 0;
        return fac[n] * invfac[n - m] % mod;
    }
};

Comb f(3e5);

const int N = 2e5 + 10;

vector<int> g[N];
i64 ans, inv;
int siz[N], n, k;

//DFS的同时计算贡献 C(n - siz[u], k / 2) * C(siz[u], k / 2)
void DFS(int u, int ufa) {
    siz[u] = 1;
    for (auto& v : g[u]) {
        if (v == ufa) continue;
        DFS(v, u);
        siz[u] += siz[v];
        ans = (ans + f.C(n - siz[v], k / 2) * f.C(siz[v], k / 2) % mod) % mod;
    }
}

void solve() {

    cin >> n >> k;
    for (int i = 1; i <= n - 1; ++i) {
        int u, v; cin >> u >> v;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }

    if (k % 2 == 0) {
        DFS(1, 0);
        //inv是逆元
        inv = f.inv(f.C(n, k));
        ans = ans * inv % mod;
    }

    cout << (ans + 1) % mod;
}

signed main() {

    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int t = 1; //cin >> t;
    while (t--) solve();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬味的橙汁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值