Blog of RuSun

\begin {array}{c} \mathfrak {One Problem Is Difficult} \\\\ \mathfrak {Because You Don't Know} \\\\ \mathfrak {Why It Is Diffucult} \end {array}

P5400 [CTS2019] 随机立方体

P5400 [CTS2019] 随机立方体

要求恰有 $k$ 个极大的数,考虑钦定 $k$ 个极大的数,剩下的随意选取,记为 $F(k)$ ,那么有 $F(k) = \sum _ {i = k} \binom i k G(i)$ ,二项式反演后就可以得到答案。

考虑如何计算 $F(k)$ 。钦定了 $k$ 个极大数后,对于每个数都有 $3$ 个面受到影响,这是很麻烦的。但是如果我们确定了 $k$ 个数的大小,即按照升序统计答案,那么前面的对后面的没有影响。

任意一个面最多只能有一个极大值,即 $k \le \min (n, m, l)$ 。选择一个坐标 $(a, b, c)$ 后,面 $x = a, y = b, z = c$ 均无法被选择,那么选择 $i$ 个极大值后,可以选择的点有 $(n - i) (m - i) (l - i)$ 个,则共 $\prod _ {i = 0} ^ {k - 1} (n - i) (m - i) (l - i)$ 。注意,我们不仅确定了选择了那些位置,还确定了相对大小顺序。

钦定后,不受到影响的数有 $g (i) = (n - i) (m - i) (l -i)$ 个,受到影响的数有 $G(i) = n m l - g(i)$ 个。在总共的 $nml$ 个数中,我们选择 $G(i)$ 个数作为这些的值,共 $\binom {nml} {G(i)}$ 种。

考虑从小到大一层一层的计算,可以参考 cmd的博客 中图片,从外到内第 $i$ 层有位置 $g(i - 1) - g(i)$ ,可以选的数的个数为 $G(i)$ ,其中最大值是确定的,即从 $G(i) - 1$ 个数中选择 $g(i - 1) - g(i) - 1$ 个,方案数为 $(G(i) - 1) ^ {\underline {g(i - 1) - g(i) - 1}}$ 。发现 $(G(i) - 1) - (g(i - 1) - g(i) - 1) = G(i) + g(i) - g(i - 1) = nml - g(i - 1) = G(i - 1)$ 。 即方案数为 $\frac {(G(i) - 1)!} {G(i - 1)!}$ 。

其他的数 $g(k)$ 随便选,即 $g(k)!$ 。

那么

$$
\begin {aligned}
F(k) & = g(k)!\binom {nml} {G(k)} \prod _ {i = 0} ^ {k - 1} g(i) \prod _ {i = 1} ^ k \frac {(G(i) - 1)!} {G(i - 1)!} \\
& = \frac {(nml)!} {G(k)!} \prod _ {i = 0} ^ {k - 1} g(i) \prod _ {i = 1} ^ k \frac {(G(i) - 1)!} {G(i - 1)!} \\
& = \frac {(nml)!} {G(0)!\prod _ {i = 1} ^ k \frac {G(i)!}{G(i - 1)!}} \prod _ {i = 0} ^ {k - 1} g(i) \prod _ {i = 1} ^ k \frac {(G(i) - 1)!} {G(i - 1)!} \\
& = (nml)!\prod _ {i = 0} ^ {k - 1} g(i)\prod _ {i = 1} ^ k \frac {(G(i) - 1)!} {Gi!}\\
& = (nml)!\prod _ {i = 0} ^ {k - 1} g(i)\prod _ {i = 1} ^ k \frac 1 {Gi}\\
\end {aligned}
$$

最终答案为

$$
\sum _ {i = k} \binom i k (-1) ^ {i - k} \prod _ {j = 0} ^ {k - 1} g(j) \prod _ {j = 1} ^ k \frac 1 {G(i)}
$$

如果线性处理 $G$ 逆元,可以做到 $(T k)$ 。

查看代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <cstdio>
#include <algorithm>
using namespace std;
template <class Type>
void read (Type &x)
{
char c;
bool flag = false;
while ((c = getchar()) < '0' || c > '9')
flag |= c == '-';
x = c - '0';
while ((c = getchar()) >= '0' && c <= '9')
x = (x << 3) + (x << 1) + c - '0';
if (flag) x = ~x + 1;
}
template <class Type, class ...Rest>
void read (Type &x, Rest &...y) { read(x); read(y...); }
template <class Type>
void write (Type x)
{
if (x < 0) putchar('-'), x = ~x + 1;
if (x > 9) write(x / 10);
putchar('0' + x % 10);
}
typedef long long LL;
const int N = 5e6 + 10, mod = 998244353;
int inv[N], fact[N], ifact[N];
int binpow (int b, int k = mod - 2)
{
int res = 1;
for (; k; k >>= 1, b = (LL)b * b % mod)
if (k & 1) res = (LL)res * b % mod;
return res;
}
void init ()
{
inv[1] = 1;
for (int i = 2; i < N; ++i)
inv[i] = -(LL)(mod / i) * inv[mod % i] % mod;
fact[0] = ifact[0] = 1;
for (int i = 1; i < N; ++i)
{
fact[i] = (LL)fact[i - 1] * i % mod;
ifact[i] = (LL)ifact[i - 1] * inv[i] % mod;
}
}
int C (int a, int b) { return a < b ? 0 : (LL)fact[a] * ifact[b] % mod * ifact[a - b] % mod; }
int main ()
{
init();
int T; read(T);
for (int n, m, l, k; T; --T)
{
read(n, m, l, k);
int res = 0;
for (int i = 1, t = 1; i <= min({n, m, l}); ++i)
{
t = (LL)t * (n - i + 1) % mod * (m - i + 1) % mod * (l - i + 1) % mod * binpow((LL)n * m % mod * l % mod - (LL)(n - i) * (m - i) % mod * (l - i) % mod) % mod;
if (i >= k) res = (res + (i - k & 1 ? -1ll : 1ll) * C(i, k) * t % mod) % mod;
}
write((res + mod) % mod), puts("");
}
return 0;
}