【CTS 2019】珍珠

Problem

Description

n n n 个在范围 [ 1 , D ] [1,D] [1,D] 内的整数均匀随机变量。

求至少能选出 m m m 个瓶子,使得存在一种方案,选择一些变量,并把选出来的每一个变量放到一个瓶子中,满足每个瓶子都恰好装两个值相同的变量的概率。

请输出概率乘上 D n D^n Dn 后对 998244353 998244353 998244353 取模的值。取模部分说明可参考 。

Range

0 ≤ m ≤ 1 0 9 , 1 ≤ n ≤ 1 0 9 , 1 ≤ D ≤ 1 0 5 0\le m\le 10^9,1\le n\le 10^9,1\le D\le 10^5 0m109,1n109,1D105

Algorithm

F F T FFT FFT ,生成函数

Mentality

对于 2 m > n 2m>n 2m>n n − 2 m ≥ D n-2m \ge D n2mD 的情况先特判掉。

不难发现,设 a i a_i ai 为颜色 i i i 出现的次数,我们要求的就是能满足 ∑ a i   m o d   2 ≤ n − 2 m \sum a_i\ mod\ 2 \le n - 2m ai mod 2n2m 的方案数。

f i f_i fi 为恰好有 i i i 种颜色出现次数为奇数的方案数,则有:

a n s = ∑ i = 0 n − 2 m f i ans = \sum_{i=0}^{n-2m} f_i ans=i=0n2mfi

写出 f i f_i fi 的表达式之后不太好求,我们需要令限制更少一点。

考虑钦定 i i i 个颜色的出现次数为奇数,设 h i h_i hi 为,对于钦定的某 i i i 种颜色,使得出现次数为奇数,且其他颜色出现次数随意的方案数。

g i = ( D i ) h i g_i = \tbinom{D}{i} h_i gi=(iD)hi ,也就是对于每种不同的钦定方案,我们将其求和。

那么根据定义,必然有等式:

g i = ∑ ( j i ) f j g_i=\sum \tbinom{j}{i} f_j gi=(ij)fj

因为每个 f j f_j fj 都会被 g i g_i gi 中的 ( j i ) \tbinom{j}{i} (ij) 种钦定方案算到。

利用指数形生成函数 ∑ c n x i i ! = e c x \sum c^n\frac{x^i}{i!} = e^{cx} cni!xi=ecx 可得:

h i = n ! [ x n ] ( e x − e − x 2 ) i ( e x ) D − i = n ! 2 i [ x n ] ∑ j = 0 i ( i j ) e j x ( − e − x ) i − j e D − i = n ! 2 i ∑ j = 0 i ( i j ) ( − 1 ) i − j [ x n ] e D + 2 j − 2 i = n ! 2 i ∑ j = 0 i ( i i − j ) ( − 1 ) j [ x n ] e D − 2 j h_i = n![x^n](\frac{e^x-e^{-x}}{2})^i(e^x)^{D-i}\\ =\frac{n!}{2^i}[x^n]\sum_{j=0}^i \tbinom{i}{j} e^{jx}(-e^{-x})^{i-j}e^{D-i}\\ =\frac{n!}{2^i}\sum_{j=0}^i \tbinom{i}{j} (-1)^{i-j}[x^n]e^{D+2j-2i}\\ =\frac{n!}{2^i}\sum_{j=0}^i \tbinom{i}{i-j} (-1)^{j}[x^n]e^{D-2j}\\ hi=n![xn](2exex)i(ex)Di=2in![xn]j=0i(ji)ejx(ex)ijeDi=2in!j=0i(ji)(1)ij[xn]eD+2j2i=2in!j=0i(iji)(1)j[xn]eD2j

随后因为 [ x n ] e c x = c n n ! [x^n]e^{cx}=\frac{c^n}{n!} [xn]ecx=n!cn ,直接代入得:

h i = n ! 2 i ∑ j = 0 i ( i i − j ) ( − 1 ) j ( D − 2 j ) n n ! = i ! 2 i ∑ j = 0 i 1 ( i − j ) ! ( − 1 ) j ( D − 2 j ) n j ! h_i =\frac{n!}{2^i}\sum_{j=0}^i \tbinom{i}{i-j} (-1)^{j}\frac{(D-2j)^n}{n!}\\ =\frac{i!}{2^i}\sum_{j=0}^i \frac{1}{(i-j)!}\frac{(-1)^j(D-2j)^n}{j!} hi=2in!j=0i(iji)(1)jn!(D2j)n=2ii!j=0i(ij)!1j!(1)j(D2j)n

这样就可以卷积了。

然后根据定义式直接算出 { g i } \{g_i\} {gi}

根据二项式反演可得:

f i = ∑ j = i D ( − 1 ) j − i ( j i ) g j = ∑ j = 0 D − i ( − 1 ) j ( j + i i ) g j + i = 1 i ! ∑ j = 0 D − i ( − 1 ) j j ! ( j + i ) ! g j + i f_i=\sum_{j=i}^{D} (-1)^{j-i} \tbinom{j}{i} g_j\\ = \sum_{j=0}^{D - i} (-1)^{j} \tbinom{j + i}{i} g_{j + i}\\ = \frac{1}{i!}\sum_{j=0}^{D - i} \frac{(-1)^{j}}{j!} (j+i)!g_{j + i}\\ fi=j=iD(1)ji(ij)gj=j=0Di(1)j(ij+i)gj+i=i!1j=0Dij!(1)j(j+i)!gj+i

p D − j − i = g j + i ( j + i ) ! p_{D-j-i}=g_{j+i}(j+i)! pDji=gj+i(j+i)!,则有:

f i = 1 i ! ∑ j = 0 D − i ( − 1 ) j j ! p D − j − i f_i= \frac{1}{i!}\sum_{j=0}^{D - i} \frac{(-1)^{j}}{j!} p_{D-j-i}\\ fi=i!1j=0Dij!(1)jpDji

这样就可以卷积了。

Code

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long
#define go(G, x, i, v) \
  for (int i = G.hd[x], v = G.to[i]; i; v = G.to[i = G.nx[i]])
#define inline __inline__ __attribute__((always_inline))
inline LL read() {
  LL x = 0, w = 1;
  char ch = getchar();
  while (!isdigit(ch)) {
    if (ch == '-') w = -1;
    ch = getchar();
  }
  while (isdigit(ch)) {
    x = (x << 3) + (x << 1) + ch - '0';
    ch = getchar();
  }
  return x * w;
}

const int Max_n = 8e5 + 5, mod = 998244353;

int D, n, m;
int fac[Max_n], ifac[Max_n];
int f[Max_n], g[Max_n], G[Max_n];

int ksm(int a, int b = mod - 2) {
  int res = 1;
  for (; b; b >>= 1, a = (LL)a * a % mod)
    if (b & 1) res = (LL)res * a % mod;
  return res;
}

namespace Poly {
int len, bit, rev[Max_n];
void init(int n) {
  len = 1 << (bit = log2(n) + 1);
  for (int i = 0; i < len; i++)
    rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit - 1);
}
void dft(int *f, bool t) {
  for (int i = 0; i < len; i++)
    if (rev[i] > i) swap(f[i], f[rev[i]]);
  for (int l = 1; l < len; l <<= 1) {
    int Wn = ksm(3, (mod - 1) / (l << 1));
    if (t) Wn = ksm(Wn);
    for (int i = 0; i < len; i += l << 1) {
      int Wnk = 1;
      for (int j = i; j < i + l; j++, Wnk = (LL)Wnk * Wn % mod) {
        int x = f[j], y = (LL)f[j + l] * Wnk % mod;
        f[j] = (x + y) % mod, f[j + l] = (x - y + mod) % mod;
      }
    }
  }
  if (t)
    for (int i = 0, Inv = ksm(len); i < len; i++) f[i] = (LL)f[i] * Inv % mod;
}
void Mul(int *f, int *g, int N) {
  init(N);
  dft(f, 0), dft(g, 0);
  for (int i = 0; i < len; i++) f[i] = (LL)f[i] * g[i] % mod;
  dft(f, 1), dft(g, 1);
}
}  // namespace Poly
using namespace Poly;

namespace Input {
void main() { D = read(), n = read(), m = read(); }
}  // namespace Input

namespace Init {
void main() {
  fac[0] = 1;
  for (int i = 1; i <= D; i++) fac[i] = (LL)fac[i - 1] * i % mod;
  ifac[D] = ksm(fac[D]);
  for (int i = D; i; i--) ifac[i - 1] = (LL)ifac[i] * i % mod;
}
}  // namespace Init

namespace Solve {
int C(int n, int m) {
  if (n < m || m < 0) return 0;
  return (LL)fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
void main() {
  if (2 * m > n) {
    puts("0");
    return;
  }
  if (D <= n - 2 * m) {
    printf("%d\n", ksm(D, n));
    return;
  }
  for (int i = 0, t = 1; i <= D; i++, t *= -1) {
    G[i] = ifac[i];
    g[i] =
        (LL)(t + mod) * ksm((D - 2 * i + mod) % mod, n) % mod * ifac[i] % mod;
  }
  Mul(G, g, D + 1 << 1);
  for (int i = 0; i <= D; i++) G[i] = (LL)ksm(ksm(2, i)) * G[i] % mod * fac[i] % mod * C(D, i) % mod;
  for (int i = 0, t = 1; i <= D; i++, t *= -1) {
    g[i] = (LL)G[D - i] * fac[D - i] % mod;
    f[i] = (LL)(t + mod) * ifac[i] % mod;
  }
  Mul(f, g, D + 1 << 1);
  int ans = 0;
  for (int i = 0; i <= n - 2 * m; i++)
    (ans += (LL)f[D - i] * ifac[i] % mod) %= mod;
  cout << ans << endl;
}
}  // namespace Solve

int main() {
#ifndef ONLINE_JUDGE
  freopen("3120.in", "r", stdin);
  freopen("3120.out", "w", stdout);
#endif
  Input::main();
  Init::main();
  Solve::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值