「TJOI2019」唱、跳、rap 和篮球

#3106. 「TJOI2019」唱、跳、rap 和篮球

Summarize

有 $n$ 个蔡徐坤,有 $m$ 个世界上最帅的欧阳,求总共有多少人。

$n,m\le1000000$

Solution

记连续的 4 个学生分别喜欢唱、跳、rap 和篮球为一组。考虑容斥,记 $f[i]$ 为保证至少存在 $i$ 组的序列方案数。根据题意,在此序列中最多只会存在 $m=\min\lbrace a,b,c,\lfloor\frac{n}{4}\rfloor\rbrace$ 组。最终答案即为 $\sum_{i=0}^{m}(-1)^{i}\times f[i]$。

考虑 $f[i]$ 的计算。由于保证至少存在 $i$ 组,可以将 $i$ 组随机插入到序列中,然后在剩下的位置插入任意学生并统计答案。

将 $i$ 个长度为 4 的组插入到长度为 $n$ 的序列中,共有 ${n-3i\choose i}$ 种方案。

将 $i_1$ 个喜欢唱、$i_2$ 个喜欢跳、$i_3$ 个喜欢 rap、$i_4$ 个喜欢篮球的学生放置在长度为 $n-4i$ 的序列中,如果保证 $i_1+i_2+i_3+i_4=n-4i$,则方案数为 $\frac{(n-4i)!}{i_1!\times i_2!\times i_3\times i_4!}$。

然而题目不保证剩余的 $a-i$、$b-i$、$c-i$、$d-i$ 个学生之和恰好为 $n-4i$,因此该部分的答案为:

$$
\sum_{i_1=0}^{a-i}\sum_{i_2=0}^{b-i}\sum_{i_3=0}^{c-i}\sum_{i_4=0}^{d-i}[i_1+i_2+i_3+i_4=n-4i]\frac{(n-4i)!}{i_1!\times i_2!\times i_3\times i_4!}
$$

注意到这是一个四元卷积的形式,可以用生成函数 + NTT 暴力求解。

Code

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll MOD = 998244353, G = 3;

inline ll qpow(ll a, ll k) {
ll ret = 1;
while (k) {
if (k & 1) ret = ret * a % MOD;
a = a * a % MOD;
k >>= 1;
}
return ret;
}

namespace FFT {
int n, r[10005];
void NTT(ll *a, int op) {
int k = 0;
for (; (1 << k) < n; ++k);
for (int i = 0; i < n; ++i) {
r[i] = r[i >> 1] >> 1 | (i & 1) << (k - 1);
if (i < r[i]) swap(a[i], a[r[i]]);
}
for (int l = 2; l <= n; l <<= 1) {
int m = l >> 1;
ll w = qpow(G, (MOD - 1) / l);
if (op == -1) w = qpow(w, MOD - 2);
for (int i = 0; i < n; i += l) {
ll wk = 1;
for (int j = 0; j < m; ++j, wk = wk * w % MOD) {
ll p = a[i + j], q = wk * a[i + j + m] % MOD;
a[i + j] = (p + q) % MOD;
a[i + j + m] = (p - q + MOD) % MOD;
}
}
}
}
void DFT(ll *a) {
NTT(a, 1);
}
void IDFT(ll *a) {
NTT(a, -1);
ll inv = qpow(n, MOD - 2);
for (int i = 0; i < n; ++i)
a[i] = a[i] * inv % MOD;
}
};

ll f[1005];
ll fac[4005], inv[4005], facinv[4005];
ll x[4005], y[4005], z[4005], w[4005];

ll C(int n, int k) {
return fac[n] * facinv[k] % MOD * facinv[n - k] % MOD;
}

int n, a, b, c, d, m;
ll ans = 0;

int main() {
fac[0] = facinv[0] = 1;
fac[1] = inv[1] = facinv[1] = 1;
for (int i = 2; i <= 4000; ++i) {
fac[i] = fac[i - 1] * 1ll * i % MOD;
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
facinv[i] = facinv[i - 1] * inv[i] % MOD;
}
scanf("%d%d%d%d%d", &n, &a, &b, &c, &d);
m = min(min(min(min(n / 4, a), b), c), d);
for (int i = 0; i <= m; ++i) {
memset(x, 0, sizeof(x));
memset(y, 0, sizeof(y));
memset(z, 0, sizeof(z));
memset(w, 0, sizeof(w));
for (int j = 0; j <= a - i; ++j) x[j] = facinv[j];
for (int j = 0; j <= b - i; ++j) y[j] = facinv[j];
for (int j = 0; j <= c - i; ++j) z[j] = facinv[j];
for (int j = 0; j <= d - i; ++j) w[j] = facinv[j];
FFT::n = 1;
while (FFT::n <= a + b + c + d - 4 * i) FFT::n <<= 1;
FFT::DFT(x), FFT::DFT(y), FFT::DFT(z), FFT::DFT(w);
for (int j = 0; j < FFT::n; ++j)
x[j] = x[j] * y[j] % MOD * z[j] % MOD * w[j] % MOD;
FFT::IDFT(x);
f[i] = C(n - 3 * i, i) * fac[n - 4 * i] % MOD * x[n - 4 * i] % MOD;
if (i & 1) ans = (ans - f[i] + MOD) % MOD;
else ans = (ans + f[i]) % MOD;
}
printf("%lld", ans);
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×