Codeforces 1156F Card Bag
题目链接
https://codeforces.com/contest/1156/problem/F
题目大意
有 $n$ 个卡片,每个卡片上的数字为 $a_i$ 。每次等概率从这对卡里面抽出一个卡,假设 $x$ 为本次抽出来的卡片, $y$ 为上一次抽出的卡片,那么如果 $x < y$ 那么你就输了;如果 $x = y$ 你就获胜;如果 $x > y$ 那么比赛会继续。问你获胜的概率为多少。
题目分析
定义 $f[i][j]$ 为取大小为 $i$ 的数 $1$ 次,且这是取出来的第 $j$ 个数的概率为多少。那么显然有:
$$f[i][j] = \sum\limits_{k=1}^{i-1}{f[k][j – 1] \cdot \frac{cnt[i]}{n – j + 1}}$$
其中 $cnt[i]$ 为 $i$ 的出现次数。
注意到其中的求和可以使用前缀和来优化。
统计答案时只需要统计其中 $cnt[i] > 1$ 且连续取出来两次的情况即可。
代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> #include <string> #include <algorithm> using namespace std; const int maxn = 5005; long long mod = 998244353; int n, m, t; long long inv[maxn]; long long cnt[maxn]; long long f[maxn][maxn]; int read(){ int x; scanf("%d", &x); return x; } long long qpow(long long b, long long x){ long long ret = 1; while(x){ if(x & 1){ ret = ret * b % mod; } x >>= 1; b = b * b % mod; } return ret; } int main(){ int i, j; int x, y; long long ans = 0; for(i=1;i<=5000;i++){ inv[i] = qpow(i, mod - 2); } n = read(); for(i=1;i<=n;i++){ x = read(); cnt[x]++; } for(i=1;i<=n;i++){ f[i][1] = cnt[i] * inv[n] % mod; if(cnt[i] > 1)ans += cnt[i] * (cnt[i] - 1) % mod * inv[n] % mod * inv[n - 1] % mod; ans %= mod; f[i][1] += f[i - 1][1]; for(j=2;j<=n;j++){ f[i][j] = f[i - 1][j - 1] * cnt[i] % mod * inv[n - j + 1] % mod; if(cnt[i] > 1)ans += f[i - 1][j - 1] * cnt[i] % mod * (cnt[i] - 1) % mod * inv[n - j + 1] % mod * inv[n - j] % mod; ans %= mod; f[i][j] += f[i - 1][j]; } } printf("%lld\n", ans); return 0; }