小红删树

小红删树

题目描述

给定一棵包含 $n$ 个节点的树,小红将进行如下操作:

  • 考虑当前森林的所有连通块,对于每个连通块,独立地、均匀随机地选择该连通块中的一个节点,之后删除所有被选中的节点(删除后其相邻边也会被移除)。

若某一轮操作开始前所有节点已被删除,则该轮及后续操作无法执行。

请你计算:在前 $2$ 次操作结束后仍有节点剩余,且在第 $3$ 次操作结束后节点全部被删除的概率。可以证明答案可以表示为一个不可约分数 $\tfrac{p}{q}$,为了避免精度问题,请直接输出整数 $\left(p \times q^{-1} \bmod M\right)$ 作为答案,其中 $M = 998\,244\,353$,$q^{-1}$ 是满足 $q\times q^{-1} \equiv 1 \pmod{M}$ 的整数。

【名词解释】

连通块:也称连通分量,满足,

  • 是原图的一个子图;
  • 连通块内的任意两个顶点之间都存在路径相连,且路径上的点也在连通块内;
  • 是极大的,即不能再通过添加原图中的其他顶点而依旧保持连通性;

单独的点也构成一个连通块。

输入描述:

第一行输入一个整数 $n\left(1 \leqq n \leqq 2\times 10^5\right)$,表示树的节点数。

接下来 $n-1$ 行,第 $i$ 行输入两个整数 $u_i,v_i \left(1 \leqq u_i, v_i \leqq n\right)$,表示树中第 $i$ 条边连接节点 $u_i$ 和节点 $v_i$。保证输入构成一棵树。

输出描述:

输出一行一个整数,代表答案对 $998\,244\,353$ 取模的结果。

示例1

输入

3
1 2
2 3

输出

665496236

说明

只有当第一次删除选择了 $2$ 点时不符合题意,此时一定会在第二次删除时删除所有节点。所以答案为 $\frac{2}{3}$。

我们能够找到,$665\,496\,236 \times 3 = 1\,996\,488\,708$,对 $998\,244\,353$ 取模后恰好等于分子 $2$,所以 $665\,496\,236$ 是需要输出的答案。

示例2

输入

4
2 3
2 4
2 1

输出

748683265

备注:

在几乎全部的情况下,PyPy 的运行速度优于 Python,我们建议您选择对应版本的 PyPy 进行提交、而不是 Python。

 

解题思路

  提供一种与官方题解不同且更加简单的做法。

  由于第 $3$ 次操作后所有节点都被删除,而每次操作每个连通块仅删除一个节点,因此第 $3$ 次操作开始前(即第 $2$ 次操作结束后),森林里只能剩下若干个孤立点(大小为 $1$ 的连通块)。这是因为在第 $3$ 次操作中,每个连通块只能删除一个节点,若某个连通块大小大于 $1$,操作后必然会有节点剩余,这与节点全部被删除的条件矛盾。

  由此反推第 $2$ 次操作开始前(即第 $1$ 次操作结束后)的图结构。我们需要找到一种连通块结构,使得删除其中一个节点后,剩余节点全部变成孤立点。这意味着该连通块中至多有一个度数大于 $1$ 的节点(称为中心点)。若度数大于 $1$ 的节点不少于两个,由于单次删除操作对节点度数的削减量至多为 $1$,则必剩余度数大于 $0$ 的节点,使得在第 $3$ 轮操作前无法形成全孤立点的情况。事实上,这种至多一个节点度数大于 $1$,其余节点度数均为 $1$ 的无向树结构被称为菊花图,如下图所示。

image

  因此,第 $1$ 次操作结束后,所有的连通块都必须是菊花图。同时,为了满足中前 $2$ 次操作结束后仍有节点剩余,第 $1$ 次操作结束后必须至少存在一个大小大于 $1$ 的连通块,否则第 $2$ 次操作会将所有大小为 $1$ 的连通块删空。

  进一步反推第 $1$ 次操作,只能删除满足以下条件的节点 $u$:删除 $u$ 后,其产生的各个连通块均为菊花图,且至少存在一个大小大于 $1$ 的连通块。我们可以枚举每个节点 $u$ 作为第 $1$ 次操作删除的节点,判断其合法性并计算概率。假设 $u$ 被删除,原树会被切分为若干个以 $u$ 的邻点 $v_i$ 为根的子树连通块。剩下的问题是如何快速判断以 $v_i$ 为根的子树是否为菊花图。

  可分为两种情况:第一种情况是 $v_i$ 是菊花图的中心点,此时子树 $v_i$ 内所有节点都直接与 $v_i$ 相连,满足 $d_{v_i} = s_{v_i}$(其中 $d_{v_i}$ 为节点 $v_i$ 的度数,$s_{v_i}$ 为子树大小);第二种情况是 $v_i$ 为菊花图的叶子节点,此时 $v_i$ 必须与菊花图的中心点 $w$ 相连,满足 $d_{v_i} = 2$ 且 $d_w = s_{v_i} - 1$。

image

  最后计算概率。对于第 $1$ 次操作选中的节点 $u$,其被选中的概率为 $\frac{1}{n}$。在第 $2$ 次操作中,对于每个大小为 $s_{v_i}$ 的菊花图连通块,为了得到孤立节点,必须删除其中心点,概率为 $\frac{1}{s_{v_i}}$(注意,若 $s_{v_i} \leq 2$ 则任意节点均可视为中心,概率为 $1$)。若删除 $u$ 后所有连通块均合法,则该情况对答案的贡献为 $\tfrac{1}{n}\prod_{v_i \in N(u)}{[d_{v_{i}} > 2]\tfrac{1}{s_{v_i}} + [d_{v_i} = 2] \cdot 1}$。若删除 $u$ 后存在不合法的连通块,或者所有连通块大小均为 $1$(即 $u$ 是原树的中心点,$d_u = n-1$),则贡献为 $0$。累加所有合法节点 $u$ 的贡献即为最终答案。

  AC 代码如下,时间复杂度为 $O(n)$:

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

typedef long long LL;

const int N = 2e5 + 5, M = N * 2, mod = 998244353;

int n;
int h[N], e[M], ne[M], idx;
int d[N], s[N];

void add(int u, int v) {
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

int qmi(int a, int k) {
    int ret = 1;
    while (k) {
        if (k & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        k >>= 1;
    }
    return ret;
}

LL get(int u, int p, int s) {
    if (d[u] == s) return d[u] == 2 ? 1 : qmi(s, mod - 2);
    if (d[u] > 2) return 0;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        if (d[v] != s - 1) return 0;
    }
    return qmi(s, mod - 2);
}

int dfs(int u, int p) {
    s[u] = 1;
    int ret = 0, pro = qmi(n, mod - 2);
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        ret = (ret + dfs(v, u)) % mod;
        s[u] += s[v];
        pro = pro * get(v, u, s[v]) % mod;
    }
    if (p) pro = pro * get(p, u, n - s[u]) % mod;
    if (d[u] == n - 1) pro = 0;
    ret = (ret + pro) % mod;
    return ret;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;
    memset(h, -1, sizeof(h));
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v), add(v, u);
        d[u]++, d[v]++;
    }
    cout << dfs(1, 0);
    
    return 0;
}

 

参考资料

  【视频题解】牛客周赛 Round 133:https://www.bilibili.com/video/BV1v3ADzZEfN/

posted @ 2026-03-05 21:20  onlyblues  阅读(2)  评论(0)    收藏  举报
Web Analytics