题解:洛谷 AT_abc463_f Senshuraku
【题目来源】
洛谷:AT_abc463_f [ABC463F] Senshuraku - 洛谷
【题目描述】
A tournament is being held with \(2N\) players. From now on, each player will play exactly one match. In the remaining \(N\) matches, the \(i\)-th \((1\le i\le N)\) match is played between the \((2i-1)\)-th and \(2i\)-th players.
In each match, one of the two competing players wins and the other loses. Which player wins is determined independently for each match, and each player wins with probability \(\dfrac12\).
Before the last \(N\) matches begin, the \(i\)-th \((1\le i\le2N)\) player has won \(A _ i\) times. After all matches are over, the champion is chosen from among the players with the most wins, uniformly at random and independently of the previous match results.
For each of the first, second, \(\ldots\), \(2N\)-th players, find the probability, modulo \(998244353\), of that player becoming the champion.
Definition of probability modulo \(998244353\)
It can be proved that the sought probability is always a rational number. Moreover, under the constraints of this problem, it can be proved that when the rational number is expressed as an irreducible fraction \(\frac{P}{Q}\), we have \(Q {{}\not\equiv{}} 0 \pmod{998244353}\). Therefore, there is a unique integer \(R\) satisfying \(R \times Q \equiv P \pmod{998244353}, 0 \leq R \lt 998244353\). Find this \(R\).
正在举行一场有 \(2N\) 名选手参加的锦标赛。从现在开始,每位选手将恰好进行一场比赛。在剩下的 \(N\) 场比赛中,第 \(i\) 场 \((1\le i\le N)\) 比赛由第 \((2i-1)\) 名选手和第 \(2i\) 名选手进行。
每场比赛中,两名参赛选手中的一人获胜,另一人失败。哪位选手获胜对每场比赛独立决定,每位选手获胜的概率均为 \(\dfrac12\)。
在最后 \(N\) 场比赛开始前,第 \(i\) 名 \((1\le i\le 2N)\) 选手已经赢了 \(A_i\) 次。所有比赛结束后,从获胜次数最多的选手中均匀随机地选出冠军,且该选择与之前的比赛结果独立。
对于第 \(1\) 名、第 \(2\) 名、\(\ldots\)、第 \(2N\) 名选手,求该选手成为冠军的概率,对 \(998244353\) 取模。
概率对 \(998244353\) 取模的定义
可以证明所求概率总是一个有理数。此外,在本题的约束条件下,可以证明当该有理数表示为最简分数 \(\frac{P}{Q}\) 时,有 \(Q \not\equiv 0 \pmod{998244353}\)。因此,存在唯一的整数 \(R\) 满足 \(R \times Q \equiv P \pmod{998244353}\),\(0 \le R < 998244353\)。求这个 \(R\)。
【输入】
The input is given from Standard Input in the following format:
\(N\)
\(A _ 1\) \(A _ 2\)
\(A _ 3\) \(A _ 4\)
\(\vdots\)
\(A _ {2N-1}\) \(A _ {2N}\)
【输出】
Output the probability of the first player becoming the champion, the probability of the second player becoming the champion, \(\ldots\), the probability of the \(2N\)-th player becoming the champion, in this order, separated by spaces, on a single line.
【输入样例】
4
1 2
3 4
2 3
1 4
【输出样例】
0 0 259959467 883862188 0 967049217 0 883862188
【核心思想】
-
问题分析:给定 \(2N\) 名选手,第 \(i\) 名选手已赢 \(A_i\) 次。还剩 \(N\) 场比赛,第 \(i\) 场在选手 \((2i-1)\) 和 \(2i\) 之间进行,每场比赛双方获胜概率均为 \(\dfrac12\)。所有比赛结束后,从获胜次数最多的选手中均匀随机选出冠军。求每名选手成为冠军的概率,对 \(998244353\) 取模。这是一个组合概率 + 分类讨论问题。
-
算法选择:
- 组合数学(Combinatorics):利用组合数枚举各种比赛结果的可能性
- 模逆元(Modular Inverse):在模质数 \(998244353\) 下进行分数运算,使用费马小定理和线性递推预处理逆元
- 分类讨论(Case Analysis):根据选手当前赢次数与最大值的关系统计配对情况,分别计算概率
-
关键步骤:
- 预处理:
- 阶乘
fac[i]和阶乘逆元ifac[i](用于计算组合数 \(C(n,m)\)) - 整数逆元
invn[i](线性递推,\(O(N)\)) - \(2\) 的幂次逆元 `ipw[i] = (\frac12)^i$(用于计算概率权重)
- 阶乘
- 找出当前最大赢次数 \(mx = \max(A_1, A_2, \ldots, A_{2N})\)
- 统计配对情况(按每对选手 \((2i-1, 2i)\) 分类):
- \(B\):两人赢次数都是 \(mx\) 的配对数
- \(S\):恰有一人赢次数是 \(mx\) 的配对数
- 核心公式 \(F(c, p)\):计算从 \(c\) 个"必选最大值"选手和 \(p\) 个"自由"配对中,某个特定选手成为冠军的概率因子
- \(F(c, p) = \sum_{i=0}^{p} C(p, i) \cdot (\frac12)^p \cdot \dfrac{1}{c+i}\)
- 物理意义:\(p\) 个自由配对中有 \(i\) 个产生新的最大值选手,此时共有 \(c+i\) 个最大值选手,均匀随机选冠军的概率为 \(\dfrac{1}{c+i}\)
- 计算概率因子:
- \(f_{same} = F(B, S)\):两人都是 \(mx\) 的配对中选手成为冠军的概率因子
- \(f_{single} = F(B+1, S-1)\):恰有一人是 \(mx\) 的配对中该 \(mx\) 选手成为冠军的概率因子
- 第一轮分配(处理赢次数为 \(mx\) 的选手):
- 两人都是 \(mx\):各自有 \(\dfrac12\) 概率赢,赢者赢次数变为 \(mx+1\),冠军在这两人中产生。每名选手概率为 \(\dfrac12 \cdot f_{same}\)
- 恰有一人是 \(mx\):该 \(mx\) 选手必须赢才能进入冠军候选。概率为 \(\dfrac12 \cdot f_{single}\)
- 其他选手:概率为 \(0\)
- 特殊情况 \(B = 0\)(没有两人都是 \(mx\) 的配对):
- 此时 \(mx-1\) 的选手有可能通过赢比赛达到 \(mx\),成为冠军候选
- 统计"基础对数" \(base\) 和"特殊对数" \(P\)(与 \(mx-1\) 选手相关的配对数)
- 计算 \(f_0 = F(base, P)\) 和 \(f_1 = F(base+1, P-1)\),对 \(mx-1\) 选手进行第二轮概率分配
- 输出答案:
ans[1], ans[2], ..., ans[2N]
- 预处理:
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),预处理 \(O(N)\),\(F\) 函数被调用常数次,每次 \(O(N)\)
- 空间复杂度:\(O(N)\),存储阶乘、逆元、答案数组等
-
组合概率与分类讨论的核心思想:
- 概率拆解:将复杂的全局概率问题拆解为每个配对的独立随机事件,利用组合数枚举所有可能的结果分布
- 对称性利用:同一配对中的两名选手地位对称,可以通过分类统计减少计算量
- 模意义下的分数运算:在模质数域下,除法转化为乘以模逆元,\(\dfrac{1}{k}\) 等价于 \(k^{mod-2} \pmod{mod}\)
- 关键公式 \(F(c,p)\):巧妙地将"多个独立伯努利试验的结果数"与"均匀随机选冠军"结合,通过求和公式一次性计算概率因子
- 边界情况处理:\(B = 0\) 时 \(mx-1\) 选手也有机会,需要额外的第二轮分配
- 适用于比赛概率、随机选举、多轮独立事件组合概率类问题
【解题思路】









【算法标签】
提高+ #组合概率
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将 int 定义为 long long,防止模运算溢出
const int N = 400005, mod = 998244353; // N: 最大规模, mod: 模数(质数)
int n, a[N]; // n: 比赛场数(总选手数=2n), a[i]: 第i名选手已赢次数
int ans[N]; // ans[i]: 第i名选手成为冠军的概率(模mod)
int fac[N], ifac[N], invn[N], ipw[N]; // fac: 阶乘, ifac: 阶乘逆元, invn: 整数逆元, ipw: 2的幂次逆元
// 快速幂:计算 a^b % mod
int qpos(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1) res = res * a % mod;
a = a * a % mod;
b /= 2;
}
return res;
}
// 组合数:C(n,m) = n! / (m! * (n-m)!) % mod
int C(int n, int m)
{
if (m < 0 || m > n) return 0;
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
// 辅助函数 F(B, S):表示 Z ~ Bin(S, 1/2) 时,E[1/(B+Z)] 的期望
// 即 F(B,S) = sum_{z=0}^{S} C(S,z) * (1/2)^S * 1/(B+z)
// 含义:从 S 场"单 M"比赛中,有 z 场胜出,总 M+1 人数为 B+z,取人数的倒数期望
int F(int c, int p)
{
if (p < 0) return 0;
int res = 0;
for (int i = 0; i <= p; i++)
// 枚举 z=i:从 p 场中选 i 场胜出,概率为 C(p,i)*(1/2)^p,再乘以 1/(c+i)
res = (res + C(p, i) * ipw[p] % mod * invn[c + i]) % mod;
return res;
}
// 更新答案:将v加到ans[x]上(取模)
void addans(int x, int v)
{
ans[x] = (ans[x] + v) % mod;
}
signed main() // 使用 signed 替代 int,因为 #define int long long
{
cin >> n;
for (int i = 1; i <= 2 * n; i++)
cin >> a[i]; // 读入每个选手的已赢次数
// 预处理阶乘
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i % mod;
// 预处理阶乘逆元(费马小定理)
ifac[n] = qpos(fac[n], mod - 2);
for (int i = n; i >= 1; i--)
ifac[i - 1] = ifac[i] * i % mod;
// 预处理整数逆元(线性递推)
invn[1] = 1;
for (int i = 2; i <= 2 * n + 2; i++)
invn[i] = (mod - mod / i) * invn[mod % i] % mod;
// 预处理2的幂次逆元:ipw[i] = (1/2)^i % mod
int inv2 = (mod + 1) / 2; // 2在模mod下的逆元
ipw[0] = 1;
for (int i = 1; i <= n; i++)
ipw[i] = ipw[i - 1] * inv2 % mod;
// 找出当前最大胜场数 M = max A_i
int mx = 0;
for (int i = 1; i <= 2 * n; i++)
mx = max(mx, a[i]);
// 统计配对情况:
// B: 两人都是 M 的比赛数(双 M 比赛)
// S: 恰有一人是 M 的比赛数(单 M 比赛)
int B = 0, S = 0;
for (int i = 1; i <= n; i++)
{
int x = 2 * i - 1, y = 2 * i; // 第i场比赛的两名选手
int c = (a[x] == mx) + (a[y] == mx); // 当前配对中 M 选手的数量
if (c == 2) ++B; // 两人都是 M
if (c == 1) ++S; // 恰有一人是 M
}
// 计算关键概率值:
// f_same = F(B, S):双 M 比赛中选手的贡献因子(1/2 * F(B,S))
// f_single = F(B+1, S-1):单 M 比赛中 M 选手的贡献因子(1/2 * F(B+1,S-1))
int f_same = (B ? F(B, S) : 0); // 两人都是 M 时的概率因子
int f_single = (S ? F(B + 1, S - 1) : 0); // 恰有一人是 M 时的概率因子
// ========== 情况一:最终最大值为 M+1 ==========
// 只有当前胜场为 M 的选手,赢下最后一场后,才可能达到 M+1
for (int i = 1; i <= n; i++)
{
int x = 2 * i - 1, y = 2 * i;
int c = (a[x] == mx) + (a[y] == mx);
if (c == 2)
{
// 双 M 比赛:无论谁赢,最终都会恰好有 1 人达到 M+1
// 该选手以 1/2 概率赢,此时总 M+1 人数为 B+Z(Z~Bin(S,1/2))
// 贡献 = 1/2 * E[1/(B+Z)] = 1/2 * F(B,S)
addans(x, inv2 * f_same % mod);
addans(y, inv2 * f_same % mod);
}
else if (c == 1)
{
// 单 M 比赛:M 选手以 1/2 概率赢达到 M+1
// 此时总 M+1 人数为 B+1+Z'(Z'~Bin(S-1,1/2))
// 贡献 = 1/2 * E[1/(B+1+Z')] = 1/2 * F(B+1, S-1)
if (a[x] == mx) addans(x, inv2 * f_single % mod);
else addans(y, inv2 * f_single % mod);
}
}
// ========== 情况二:最终最大值为 M(仅当 B=0 时可能) ==========
// 若存在一场双 M 比赛,则必定产生 M+1,最终最大值不可能是 M
// 因此当 B>0 时,这部分贡献为 0
if (B == 0)
{
// 为了让最终最大值仍为 M,所有当前为 M 的选手都必须输掉最后一场
// 该事件发生的概率为 (1/2)^S
int base = 0, P = 0; // base: 必定达到 M 的人数, P: "没有 M 且恰好一个 M-1"的比赛数
for (int i = 1; i <= n; i++)
{
int x = 2 * i - 1, y = 2 * i;
int c = (a[x] == mx) + (a[y] == mx); // M 选手数
int d = (a[x] == mx - 1) + (a[y] == mx - 1); // M-1 选手数
if (c == 1)
{
// 单 M 比赛:M 选手必须输,该比赛至少贡献 1 个胜场为 M 的人
// 若另一人原本是 M-1,他赢后达到 M,贡献 2 人
++base;
if (d == 1) ++base; // 另一人是 M-1,赢后达到 M
}
else
{
// 没有 M 的比赛
if (d == 2) ++base; // 两人都是 M-1:无论谁赢,贡献 1 人达到 M
else if (d == 1) ++P; // 恰有一人是 M-1:该选手以 1/2 概率赢并达到 M
}
}
// 在最终最大值为 M 的条件下,最高胜场人数为 base + Z,其中 Z ~ Bin(P, 1/2)
// 计算相关概率因子
int coef = ipw[S]; // 系数:(1/2)^S,即所有 M 选手都输的概率
int f0 = F(base, P); // 基础概率:E[1/(base+Z)]
int f1 = (P ? F(base + 1, P - 1) : 0); // 特殊概率:某场确定胜出后,E[1/(base+1+Z')]
// 分配最终最大值为 M 时的贡献
for (int i = 1; i <= n; i++)
{
int x = 2 * i - 1, y = 2 * i;
int c = (a[x] == mx) + (a[y] == mx); // M 选手数
int d = (a[x] == mx - 1) + (a[y] == mx - 1); // M-1 选手数
if (c == 1)
{
// 单 M 比赛:M 选手必定输,若另一人是 M-1 则他赢后达到 M
// 这两人必定成为最高胜场者(胜场为 M)
if (a[x] == mx || a[x] == mx - 1) addans(x, coef * f0 % mod);
if (a[y] == mx || a[y] == mx - 1) addans(y, coef * f0 % mod);
}
else
{
// 没有 M 的比赛
if (d == 2)
{
// 两人都是 M-1:各以 1/2 概率赢并达到 M
// 贡献 = (1/2)^S * 1/2 * F(base, P)
addans(x, coef * inv2 % mod * f0 % mod);
addans(y, coef * inv2 % mod * f0 % mod);
}
else if (d == 1)
{
// 恰有一人是 M-1:该选手必须赢才能达到 M
// 此时本场对应的随机变量已确定,贡献 = (1/2)^S * 1/2 * F(base+1, P-1)
if (a[x] == mx - 1) addans(x, coef * inv2 % mod * f1 % mod);
else addans(y, coef * inv2 % mod * f1 % mod);
}
}
}
}
// 输出所有选手成为冠军的概率
// 答案 = 情况一贡献 + 情况二贡献(当 B>0 时情况二为 0)
for (int i = 1; i <= 2 * n; i++)
cout << ans[i] << " ";
cout << endl;
return 0;
}
【运行结果】
4
1 2
3 4
2 3
1 4
0 0 259959467 883862188 0 967049217 0 883862188
浙公网安备 33010602011771号