牛牛的三角形

牛牛的三角形

题目描述

从集合 $\{1, 2, \dots, n\}$ 中等概率地随机选取 $m$ 个不同的整数,使得在选出的这 $m$ 个数中,存在三个不同的数 $a, b, c$,满足以 $a, b, c$ 为边长可以构成一个非退化的三角形。

求解上述情况出现的概率。可以证明答案可以表示为一个不可约分数 $\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}$ 的整数。更具体地,你需要找到一个整数 $x \in [0, M)$ 满足 $x \times q$ 对 $M$ 取模等于 $p$。

【名词解释】

非退化三角形:三条边长均大于 $0$ 且任意两边之和均大于第三边的三角形。

输入描述:

每个测试文件均包含多组测试数据。第一行输入一个整数 $T\left(1\leq T\leq 3 \times 10^5\right)$ 代表数据组数,每组测试数据描述如下:

第一行输入两个整数 $n,m\left(1\leq m\leq n\leq 1.5 \times 10^3\right)$,表示正整数的范围、随机选出的数的数量。

输出描述:

对于每一组测试数据,新起一行输出一个整数,表示答案。

示例1

输入

2
3 3
4 3

输出

0
748683265

说明

对于第二组测试数据,有以下四种不同的选取方式:

  • $\{1,2,3\}$,此时三元组 $(1,2,3)$ 无法构成非退化三角形;
  • $\{1,2,4\}$,此时三元组 $(1,2,4)$ 无法构成非退化三角形;
  • $\{1,3,4\}$,此时三元组 $(1,3,4)$ 无法构成非退化三角形;
  • $\{2,3,4\}$,此时三元组 $(2,3,4)$ 可以构成非退化三角形。

因此,概率为 $\tfrac{1}{4}$,我们能够找到,$748\,683\,265 \times 4 = 2\,994\,733\,060$,对 $998\,244\,353$ 取模后恰好等于分子 $1$,所以 $748\,683\,265$ 是需要输出的答案。

 

解题思路

  用古典概率的做法即可,要求的概率等于 $\tfrac{\text{存在非退化三角形的方案数}}{\text{总方案数}}$,其中总方案是 $C_{n}^{m}$,而存在非退化三角形的方案数较难直接求得。因此我们可以尝试计算其对立事件“不存在非退化三角形的方案数”,这样就可以用总方案数,减去不存在非退化三角形的方案数,来得到存在非退化三角形的方案数。

  由于我们只关心选出来了哪些数,而不关心选出的数的顺序,因此规定选出的数 $a_i$ 满足 $a_i < a_{i+1} \, (1 \leq i < m)$。如果选出的 $m$ 个数中不存在可以构成非退化三角形的三个数,那么对于所有相邻的三个数 $a_i,a_{i+1},a_{i+2}$,都有 $a_{i}+a_{i+1} \leq a_{i+2}$。其中如果满足 $a_{i} + a_{i+1} = a_{i+2}$,则这些数的数量为最大,同时该形式也是斐波那契数列的定义。由于 $n \leq 1500$,因此对多能取出 $15$ 个数(1 2 3 5 8 13 21 34 55 89 144 233 377 610 987),这些数中的任意三个数都不能构成非退化三角形。这也意味着如果 $m > 15$,则选出的任何 $m$ 个数中必定存在三个数构成非退化三角形,此时要求的概率为 $1$。

  所以现在我们只需考虑 $m \leq 15$ 的情况,求出从 $n$ 个数中选出 $m$ 个的方案数中,满足所有相邻三数不构成非退化三角形的方案数。考虑动态规划,定义状态 $f(i,j,k)$ 表示从 $1 \sim j$ 中选出 $i$ 个数,使其满足 $a_{u} + a_{u+1} \leq a_{u+2}$(即相邻三个数不构成非退化三角形),且最后一个数是 $j$,倒数第二个数是 $k$ 的方案数。根据倒数第三个数选什么进行状态划分,状态转移方程为 $f(i,j,k) = \sum\limits_{u=1}^{\min\{k-1,j-k\}}{f(i-1,k,u)}$,其中我们需要对枚举 $u$ 的部分进行优化。只需定义 $g(i,j,k) = \sum\limits_{u=1}^{k}{f(i,j,u)}$,则有 $f(i,j,k) = g(i-1,k,\min\{k-1,j-k\})$,从而实现 $O(1)$ 转移。

  因此不存在非退化三角形的方案数是 $\sum\limits_{i=1}^{n}{\sum\limits_{j=1}^{i-1}{f(m,i,j)}} = \sum\limits_{i=1}^{n}{g(m,i,i-1)}$,那么 $C_{n}^{m} - \sum\limits_{i=1}^{n}{g(m,i,i-1)}$ 就是存在非退化三角形的方案数,$\frac{C_{n}^{m} - \sum\limits_{i=1}^{n}{g(m,i,i-1)}}{C_{n}^{m}}$ 就是要求的概率。

  我们可以用 $O(n^2\log{n})$ 的复杂度预处理出所有的 $f(i,j,k)$,但由于有多组测试样例,若在计算时用 $O(n)$ 的复杂度来累加 $g(m,i,i-1)$,则复杂度为 $O(Tn)$,会超时。继续定义 $s(i,j) = \sum\limits_{k=1}^{j}{g(i,k,k-1)}$,这样就可以用 $O(1)$ 复杂度求出概率 $\frac{C_{n}^{m} - s(m,n)}{C_{n}^{m}}$ 了。

  AC 代码如下,时间复杂度为 $O(n^2 \log{n} + T \log{\mathrm{mod}})$:

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

typedef long long LL;

const int N = 1505, mod = 998244353;

int f[2][N][N], g[2][N][N], s[16][N];
int fact[N], inv[N];

void init() {
    for (int i = 1; i < N; i++) {
        for (int j = 0; j < i; j++) {
            g[1][i][j] = 1;
        }
        s[1][i] = i;
    }
    for (int i = 2; i <= 15; i++) {
        for (int j = 1; j < N; j++) {
            for (int k = 1; k < j; k++) {
                f[i & 1][j][k] = g[i - 1 & 1][k][min(k - 1, j - k)];
            }
        }
        for (int j = 1; j < N; j++) {
            g[i & 1][j][0] = 0;
            for (int k = 1; k < j; k++) {
                g[i & 1][j][k] = (g[i & 1][j][k - 1] + f[i & 1][j][k]) % mod;
            }
            s[i][j] = (s[i][j - 1] + g[i & 1][j][j - 1]) % mod;
        }
    }
    fact[0] = 1;
    for (int i = 1; i < N; i++) {
        fact[i] = 1ll * fact[i - 1] * i % mod;
    }
    inv[0] = inv[1] = 1;
    for (int i = 2; i < N; i++) {
        inv[i] = LL(mod - mod / i) * inv[mod % i] % mod;
    }
    for (int i = 1; i < N; i++) {
        inv[i] = 1ll * inv[i - 1] * inv[i] % mod;
    }
}

int C(int a, int b) {
    return 1ll * fact[a] * inv[a - b] % mod * inv[b] % mod;
}

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;
}

void solve() {
    int n, m;
    cin >> n >> m;
    if (m > 15) cout << 1 << '\n';
    else cout << LL(C(n, m) - s[m][n] + mod) % mod * qmi(C(n, m), mod - 2) % mod << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    init();
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  小白月赛 120 出题人题解:https://ac.nowcoder.com/discuss/1548970

posted @ 2025-09-06 23:32  onlyblues  阅读(39)  评论(0)    收藏  举报
Web Analytics