reality
数学老师上课提到的东西。
考虑一个 \(1:1\) 赔率的游戏,结果为 \(\{0, 1\}\)。那么采取以下策略:
-
第一轮赌注为 \(1\) 元。
-
如果某局输了,设这局的赌注为 \(x\),则下一轮的赌注为 \(2x\)。、
-
如果某局赢了,那么下局的赌注为 \(1\) 元。
该策略下,只要某局赢了,则你净赚 \(1\) 元。可以改成 \(kx\)。
Problem: 给出正整数 \(n, k\),定义数组 \(a = (a_1, a_2, ..., a_n), b = (b_1, b_2, ..., b_n)\),且满足 \(a_i = 2i- 1, b_i = 2i\)。对 \(a, b\) 任意重排后得到 \(a', b'\),求出 \(\sum_{i = 1}^n [a'_i > b'_i] \ge k\) 的概率。
首先问题等价于只重排 \(b\) 的问题。可以重写问题为:\(b\) 是一个长度为 \(n\) 的 \(1 \sim n\) 的排列,问满足条件 \(b_i < i\) 的位置数量 \(\ge k\) 的概率。
不妨直接对于 \(k \in [0, n]\),求出 \(= k\) 的方案数。考虑二项式反演,问题转化为求出钦定 \(k\) 个位置满足条件 \(b_i\) 的方案数。
注意到对于 \([1, i - 1]\),已经有 \(j\) 个位置 \(p_1, p_2, ..., p_j\) 满足条件,则 \(\forall t \in [1, j], b_{p_t} < i\)。换句话说,假设现在钦定 \(i\) 满足条件,则现在只有 \(i - 1 - j\) 个值可以填在这个位置上了。直接设 \(f_{i, j}\) 为考虑前 \(i\) 个数,钦定了 \(j\) 个位置满足条件的方案数(不考虑不被钦定的数的贡献),则转移为:
-
钦定 \(b_i < i\):\(f_{i, j} \gets f_{i - 1, j - 1} \times (i - j)\)
-
无限制:\(f_{i, j} \gets f_{i - 1, j}\)
最后任意排列剩下 \(n - j\) 个数,则钦定 \(j\) 个位置满足条件的概率就是 \((n - j)!f_{n, j}\),然后做一个二项式反演就行了。
code
#include<bits/stdc++.h>
#define ll long long
#define pb emplace_back
#define pir pair<int, ll>
#define fi first
#define se second
#define inv(x) qpow(x, mod - 2)
#define il inline
#define mkpir make_pair
using namespace std;
const int N = 5e3 + 10, M = 2e5 + 10;
const ll mod = 998244353;
/*
struct edge{
int v, next;
}edges[M << 1];
int head[N], idx;
void add_edge(int u, int v){
edges[++idx] = {v, head[u]};
head[u] = idx;
}
*/
il ll qpow(ll x, ll y){
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
il void chkmin(ll& x, ll y){if(y < x) x = y;}
il void chkmax(ll& x, ll y){if(y > x) x = y;}
il void chkmin(int& x, int y){if(y < x) x = y;}
il void chkmax(int& x, int y){if(y > x) x = y;}
il void ADD(ll& x, ll y){x += y; (x >= mod) ? (x -= mod) : 0;}
il void MUL(ll& x, ll y){x = x * y % mod;}
//#define int long long
int n, k;
ll f[N][N], fac[N], ifac[N];
ll binom(int x, int y){
if(x < y || y < 0 || x < 0) return 0;
return fac[x] * ifac[x - y] % mod * ifac[y] % mod;
}
void solve(){
cin >> n >> k; ifac[0] = fac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2);
for(int i = n - 1; i; i--) ifac[i] = ifac[i + 1] * (i + 1) % mod;
f[0][0] = 1;
for(int i = 1; i <= n; i++)
for(int j = 0; j <= i; j++){
f[i][j] = f[i - 1][j];
if(j) ADD(f[i][j], f[i - 1][j - 1] * (i - j) % mod);
} ll ans = 0;
for(int i = 0; i <= n; i++){
ll sum = 0;
for(int j = i; j <= n; j++){
ll tmp = binom(j, i) * f[n][j] % mod * fac[n - j] % mod;
if((j - i + 1) & 1) ADD(sum, tmp);
else ADD(sum, mod - tmp);
} if(i >= k) ADD(ans, sum);
} cout << ans * ifac[n] % mod;
}
void clr(){
}
signed main(){
freopen("qwq.in", "r", stdin);
freopen("qwq.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T = 1; while(T--) solve(), clr();
return 0;
}
有点神了。我发现 \((n - j)!f_{n, j}\) 就是恰好 \(j\) 个满足条件的方案数,然后问 AI,AI 跟我说这个东西是欧拉数,可以 \(O(n)\)。晚点研究了。

浙公网安备 33010602011771号