Luogu P4705 玩游戏

题目描述

https://www.luogu.com.cn/problem/P4705

简要题意:现在有两个序列 $a$ 和 $b$,长度分别为 $n$ 和 $m$,现在随机从 $a$ 中选一个数 $x$,然后再随机从 $b$ 中选一个数
$y$,求对于 $k\in [1,t]$,$(x+y)^k$ 的期望是多少,答案对 $998244353$ 取模

$a,b,t\le 10^5$

Solution

我们将 $(x+y)^k$ 拆开,然后得到 $ans_k=\sum_{x=1}^n\sum_{y=1}^m\sum_{i=0}^k\binom{k}{i}a_x^ib_y^{k-i}$,稍微交换一下求和顺序,$ans_k=\sum_{i=0}^n\binom{k}{i}(\sum_{i=1}^na_x^i)(\sum_{j=1}^mb_y^{k-i})$,我们令 $A_k$ 表示 $\sum_{i=1}^na_i^k$,如果我们能预处理 $A_0$ 到 $A_1$,那么 $ans$ 我们可以直接卷积

我们现在考虑如何求 $A_k$,我们令 $A_k$ 的生成函数为 $F(x)$,容易得到 $F(x)=\sum_{i=1}^n\frac{1}{1-a_ix}$,发现对于分数的求和并没有什么特别好的做法,一个比较简单的做法是类似于分治+NTT的做法,我们直接维护分子和分母这两个多项式,然后左右两边乘的时候暴力通分即可

我们接下来考虑一个比较神奇的做法,我们考虑将这个 $\sum$ 变成 $\prod$,实现这个的最好做法是 $\ln$,但原始并没有 $\ln$,所以我们造一个 $\ln$,我们知道 $\ln’(1-a_ix)=\frac{-a_i}{1-a_ix}$,我们令 $G(x)=\sum_{i=1}^n\ln’(1-a_ix)$,那么我们有 $F(x)=-xG(x)+n$

而 $G(x)=\sum_{i=1}^n\ln’(1-a_ix)=\ln’(\prod_{i=1}^n1-a_ix)$,这个直接分治+NTT加多项式 $\ln$ 即可

时间复杂度 $O(n\log^2 n)$

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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <iostream>
#include <vector>
#include <algorithm>
#define maxn 100010
#define ll long long
using namespace std;

const int p = 998244353;
ll pow_mod(ll x, ll n) {
ll s = 1;
for (; n; n >>= 1, x = x * x % p)
if (n & 1) s = s * x % p;
return s;
}

#define Poly vector<int>
#define len(A) ((int) A.size())
namespace Pol {
inline int add(int a, int b) { return (a += b) >= p ? a -= p : a; }
inline int mul(int a, int b) { return 1ll * a * b % p; }
Poly operator - (const int &v, const Poly &a) {
Poly res(a);
for (int i = 0; i < len(res); ++i) res[i] = p - res[i];
res[0] = add(res[0], v); return res;
}
Poly operator - (const Poly &a, const int &v) {
Poly res(a); res[0] = add(res[0], p - v); return res;
}
Poly operator * (const Poly &a, const int &v) {
Poly res(a);
for (int i = 0; i < len(res) ; ++i) res[i] = mul(res[i], v);
return res;
}

const int N = 4200000;
int P[N];
void init_P(int n) {
int l = 0; while ((1 << l) < n) ++l;
for (int i = 0; i < n; ++i) P[i] = (P[i >> 1] >> 1) | ((i & 1) << l - 1);
}
void NTT(Poly &a, int type) {
static int w[N]; ll G = 3, Gi = pow_mod(G, p - 2); int n = len(a);
for (int i = 0; i < n; ++i) if (i < P[i]) swap(a[i], a[P[i]]);
for (int i = 2, m = 1; i <= n; m = i, i *= 2) {
ll wn = pow_mod(type > 0 ? G : Gi, (p - 1) / i);
w[0] = 1; for (int j = 1; j < m; ++j) w[j] = wn * w[j - 1] % p;
for (int j = 0; j < n; j += i)
for (int k = 0; k < m; ++k) {
int t1 = a[j + k], t2 = 1ll * a[j + k + m] * w[k] % p;
a[j + k] = add(t1, t2);
a[j + k + m] = add(t1, p - t2);
}
}
if (type < 0) {
int inv = pow_mod(n, p - 2);
for (int i = 0; i < n; ++i) a[i] = mul(a[i], inv);
}
}
Poly operator * (const Poly &A, const Poly &B) {
int n = 1, n1 = len(A), n2 = len(B); while (n < n1 + n2 - 1) n <<= 1; init_P(n);
Poly a(n), b(n);
for (int i = 0; i < n1; ++i) a[i] = add(A[i], p);
for (int i = 0; i < n2; ++i) b[i] = add(B[i], p);
NTT(a, 1); NTT(b, 1);
for (int i = 0; i < n; ++i) a[i] = mul(a[i], b[i]);
NTT(a, -1); return a;
}
Poly Der(const Poly &a) {
Poly res(a);
for (int i = 0; i < len(a) - 1; ++i) res[i] = mul(i + 1, res[i + 1]);
res[len(a) - 1] = 0; return res;
}
Poly Int(const Poly &a) {
static int inv[N];
if (!inv[1]) {
inv[1] = 1;
for (int i = 2; i < N; ++i) inv[i] = mul(p - p / i, inv[p % i]);
}
Poly res(a); res.resize(len(a) + 1);
for (int i = len(a); i; --i) res[i] = mul(res[i - 1], inv[i]);
res[0] = 0; return res;
}
Poly Inv(const Poly &a) {
Poly res(1, pow_mod(a[0], p - 2));
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
int L = 2 * k; init_P(L); Poly t(L);
copy_n(a.begin(), min(k, len(a)), t.begin());
t.resize(L); res.resize(L);
NTT(res, 1); NTT(t, 1);
for (int i = 0; i < L; ++i) res[i] = mul(res[i], add(2, p - mul(t[i], res[i])));
NTT(res, -1); res.resize(k);
} res.resize(len(a)); return res;
}
pair<Poly, Poly> Divide(const Poly &a, const Poly &b) {
int n = len(a), m = len(b);
Poly t1(a.rbegin(), a.rbegin() + n - m + 1), t2(b.rbegin(), b.rend()); t2.resize(n - m + 1);
Poly Q = Inv(t2) * t1; Q.resize(n - m + 1); reverse(Q.begin(), Q.end());
Poly R = Q * b; R.resize(m - 1); for (int i = 0; i < len(R); ++i) R[i] = add(a[i], p - R[i]);
return make_pair(Q, R);
}
Poly Ln(const Poly &a) {
Poly res = Int(Der(a) * Inv(a));
res.resize(len(a)); return res;
}
Poly Exp(const Poly &a) {
Poly res(1, 1);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()); t.resize(k); t = Ln(t);
for (int i = 0; i < min(len(a), k); ++i) t[i] = add(a[i], p - t[i]); t[0] = add(t[0], 1);
res = res * t; res.resize(k);
} res.resize(len(a)); return res;
}
Poly Sqrt(const Poly &a) { // a[0] = 1
Poly res(1, 1); ll inv2 = pow_mod(2, p - 2);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()), ta(a.begin(), a.begin() + min(len(a), k));
t.resize(k); t = Inv(t) * ta;
res.resize(k); for (int i = 0; i < k; ++i) res[i] = mul(add(res[i], t[i]), inv2);
} res.resize(len(a)); return res;
}
Poly Pow(const Poly &a, int k) { // a[0] = 1
return Exp(Ln(a) * k);
}
Poly Pow(const Poly &a, int k, int kk) {
int n = len(a), t = n, m, v, inv, powv; Poly res(n);
for (int i = n - 1; ~i; --i) if (a[i]) t = i, v = a[i];
if (k && t >= (n + k - 1) / k) return res;
if (t == n) { if (!k) res[0] = 1; return res; }
m = n - t * k; res.resize(m);
inv = pow_mod(v, p - 2); powv = pow_mod(v, kk);
for (int i = 0; i < m; ++i) res[i] = mul(a[i + (k > 0) * t], inv);
res = Exp(Ln(res) * k); res.resize(n);
for (int i = m - 1; ~i; --i) {
ll tmp = mul(res[i], powv);
res[i] = 0, res[i + t * k] = tmp;
}
return res;
}
} // namespace Pol

int n, m, k, a[maxn], b[maxn];

Poly solve(int *a, int l, int r) {
if (l == r) {
Poly res; res.push_back(1); res.push_back(p - a[l]);
return res;
} int m = l + r >> 1;
return Pol::operator*(solve(a, l, m), solve(a, m + 1, r));
}

ll fac[maxn], inv[maxn];
void init_C(int n) {
fac[0] = 1; for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % p;
inv[n] = pow_mod(fac[n], p - 2); for (int i = n - 1; ~i; --i) inv[i] = inv[i + 1] * (i + 1) % p;
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= m; ++i) cin >> b[i];
cin >> k; init_C(k); Poly A = solve(a, 1, n), B = solve(b, 1, m);
A.resize(k + 1); B.resize(k + 1);
A = Pol::Der(Pol::Ln(A)); B = Pol::Der(Pol::Ln(B));
for (int i = k; i; --i) A[i] = -A[i - 1], B[i] = -B[i - 1]; A[0] = n; B[0] = m;
for (int i = 0; i < k + 1; ++i) A[i] = A[i] * inv[i] % p, B[i] = B[i] * inv[i] % p;
Poly res = Pol::operator*(A, B); ll invnm = pow_mod(1ll * n * m % p, p - 2);
for (int i = 1; i <= k; ++i) cout << (res[i] * invnm % p * fac[i] % p + p) % p << "\n";
return 0;
}