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)\)。晚点研究了。

posted @ 2026-06-03 15:03  Little_corn  阅读(19)  评论(1)    收藏  举报