「解题报告」ARC154F Dice Game
看起来就多项式,跟概率有关就上概率生成函数吧。
考虑类似于 Flip Cells 的套路,设 \(F(x)\) 为翻出所有的生成函数,\(G(x)\) 为第一次翻出所有的生成函数,\(H(x)\) 是翻出后任意翻的生成函数,那么有 \(F(x)=G(x)H(x)\)。
\(H(x)\) 显然等于 \(\frac{1}{1-x}\),即全翻出来之后可以随便翻,考虑 \(F(x)\)。\(F(x)\) 可以看作是每一面至少翻出了 \(1\) 次,然后将操作序列合并起来。先写出 EGF 的形式,即 \((e^{\frac{x}{n}} - 1)^n\)。然后转成 OGF,即 \(\sum_{i=0}^n \binom{n}{i}(-1)^{n-i}\frac{1}{1-\frac{ix}{n}}\)。这个东西可以分治乘法求出 \(\frac{A(x)}{B(x)}\) 的形式。那么我们就有 \(G(x)=(1-x)F(x)\) 了。
然后考虑如何求 \(k\) 次方的贡献。有个结论,\([x^k]F(e^x)\) 就是 \(k\) 次方的期望。证明展开。
考虑怎么求 \(F(e^x)\),同样可以先转成 OGF,同样的分治乘法的办法求出来之后再转回去。然后就能求出答案了。
用这个式子求出来之后会发现分子分母都没有常数项,需要上下同时除以一个 \(x\)。
人傻常数大,还需要卡常。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 600005, P = 998244353, G = 3;
int qpow(int a, int b) {
int ans = 1;
while (b) {
if (b & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P;
b >>= 1;
}
return ans;
}
const int GI = qpow(G, P - 2);
int r[MAXN];
struct Polynomial {
vector<int> a;
int len;
int& operator[](int b) { return a[b]; }
Polynomial(int len = 0) : len(len) { a.resize(len + 1); }
Polynomial(initializer_list<int> l) : len(l.size() - 1), a(l) {}
Polynomial& set(int b) { len = b, a.resize(b + 1); return *this; }
static Polynomial resize(Polynomial a, int s) { Polynomial b = a; return b.set(s); }
void reverse() { std::reverse(a.begin(), a.end()); }
static void calcRev(int limit) {
for (int i = 1; i < limit; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) * limit >> 1);
}
void ntt(int limit, bool rev) {
set(limit);
for (int i = 0; i < limit; i++)
if (i < r[i]) swap(a[i], a[r[i]]);
for (int mid = 1; mid < limit; mid <<= 1) {
int step = qpow(rev ? GI : G, (P - 1) / (mid << 1));
for (int l = 0; l < limit; l += (mid << 1)) {
int w = 1;
for (int j = 0; j < mid; j++, w = 1ll * w * step % P) {
int x = a[l + j], y = 1ll * w * a[l + j + mid] % P;
a[l + j] = (x + y) % P, a[l + j + mid] = (1ll * P + x - y) % P;
}
}
}
int invn = qpow(limit, P - 2);
if (rev) {
for (int i = 0; i < limit; i++)
a[i] = 1ll * a[i] * invn % P;
}
}
void print() { for (int i : a) printf("%d ", i); printf("\n"); }
Polynomial operator*(Polynomial b) {
Polynomial a = *this, c;
int n = a.len + b.len;
int limit = 1;
while (limit <= n) limit <<= 1;
c.set(limit);
calcRev(limit);
a.ntt(limit, 0), b.ntt(limit, 0);
for (int i = 0; i <= limit; i++) c[i] = 1ll * a[i] * b[i] % P;
c.ntt(limit, 1);
c.set(n);
return c;
}
Polynomial operator*(int b) {
Polynomial c = *this;
for (int& i : c.a) i = 1ll * i * b % P;
return c;
}
Polynomial operator+(int b) {
Polynomial c = *this;
c[0] = (1ll * c[0] + b + P) % P;
return c;
}
Polynomial operator+(Polynomial b) {
Polynomial c = *this;
c.set(max(c.len, b.len));
for (int i = 0; i <= b.len; i++) c[i] = (c[i] + b[i]) % P;
return c;
}
Polynomial operator-(Polynomial b) {
Polynomial c = *this;
c.set(max(c.len, b.len));
for (int i = 0; i <= b.len; i++) c[i] = (c[i] - b[i] + P) % P;
return c;
}
Polynomial inv(int n) {
Polynomial b;
b[0] = qpow(a[0], P - 2);
for (int d = 2; d < (n << 1); d <<= 1) {
Polynomial a = *this;
a.set(d - 1);
int limit = d << 1;
calcRev(limit);
a.ntt(limit, 0), b.ntt(limit, 0);
for (int i = 0; i < limit; i++) b[i] = (2ll - 1ll * a[i] * b[i] % P + P) % P * b[i] % P;
b.ntt(limit, 1);
b.set(d - 1);
}
b.set(n - 1);
return b;
}
Polynomial sqrt(int n) {
static int TWOINV = qpow(2, P - 2);
Polynomial b;
b[0] = 1;
for (int d = 2; d < (n << 1); d <<= 1) {
Polynomial a = *this, c = b.inv(d);
a.set(d - 1);
int limit = d << 1;
calcRev(limit);
a.ntt(limit, 0), c.ntt(limit, 0);
for (int i = 0; i < limit; i++) a[i] = 1ll * a[i] * c[i] % P;
a.ntt(limit, 1);
b.set(d - 1);
for (int i = 0; i < d; i++) b[i] = 1ll * (a[i] + b[i]) * TWOINV % P;
}
b.set(n - 1);
return b;
}
Polynomial derivative() {
Polynomial b(len - 1);
for (int i = 1; i <= len; i++) b[i - 1] = 1ll * a[i] * i % P;
return b;
}
Polynomial integral() {
Polynomial b(len + 1);
for (int i = 0; i <= len; i++) b[i + 1] = 1ll * a[i] * qpow(i + 1, P - 2) % P;
return b;
}
Polynomial ln(int n) {
return (derivative() * inv(n)).integral().set(n - 1);
}
Polynomial exp(int n) {
Polynomial b;
b[0] = 1;
for (int d = 1; d < (n << 1); d <<= 1) {
Polynomial a = *this, e = b.ln(d);
a.set(d - 1);
b = b * (e * (P - 1) + a + 1);
b.set(d - 1);
}
b.set(n - 1);
return b;
}
Polynomial pow(int b, int n) {
return (ln(n) * b).exp(n);
}
pair<Polynomial, Polynomial> operator/(Polynomial b) {
int n = len, m = b.len;
Polynomial ra = *this, rb = b, rc, d;
ra.reverse(), rb.reverse();
rc = ra * rb.inv(n - m + 1);
rc.set(n - m);
rc.reverse();
d = *this - b * rc;
d.set(m - 1);
return make_pair(rc, d);
}
} a;
int n, m;
int fac[MAXN], inv[MAXN];
void pre(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = 1ll * fac[i - 1] * i % P;
}
inv[n] = qpow(fac[n], P - 2);
for (int i = n; i >= 1; i--) {
inv[i - 1] = 1ll * inv[i] * i % P;
}
}
int C(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
return 1ll * fac[n] * inv[m] % P * inv[n - m] % P;
}
pair<Polynomial, Polynomial> solve(int l, int r) {
if (l == r) {
if (l == 0) {
return {
Polynomial{ (int) (C(n, l) * (((n - l) & 1) ? P - 1ll : 1ll) % P) },
Polynomial{ 1 }
};
}
return {
Polynomial{ (int) (C(n, l) * (((n - l) & 1) ? P - 1ll : 1ll) % P) },
Polynomial{ 1, (int) ((P - 1ll) * l % P * qpow(n, P - 2) % P) }
};
}
int mid = (l + r) >> 1;
auto a = solve(l, mid), b = solve(mid + 1, r);
if (a.first.len == 0 && a.first[0] == 0) return b;
return { a.first * b.second + a.second * b.first, a.second * b.second };
}
pair<Polynomial, Polynomial> solve2(Polynomial &p, int l, int r) {
if (l == r) {
if (l == 0) {
return { Polynomial{ p[l] }, Polynomial{ 1 } };
}
return {
Polynomial{ p[l] },
Polynomial{ 1, P - l }
};
}
int mid = (l + r) >> 1;
auto a = solve2(p, l, mid), b = solve2(p, mid + 1, r);
if (a.first.len == 0 && a.first[0] == 0) return b;
return { a.first * b.second + a.second * b.first, a.second * b.second };
}
Polynomial solve3(Polynomial f) {
auto g = solve2(f, 0, f.len);
// g.first.print();
// g.second.print();
auto h = (g.first * g.second.inv(m + 1)).set(m + 1);
for (int i = 0; i <= h.len; i++) {
h[i] = 1ll * h[i] * inv[i] % P;
}
return h;
}
int main() {
scanf("%d%d", &n, &m);
pre(n + m);
auto f = solve(0, n);
f.first = solve3(f.first * Polynomial{ 1, P - 1 });
f.second = solve3(f.second);
auto a = Polynomial(f.first.len - 1);
for (int i = 1; i <= f.first.len; i++)
a[i - 1] = f.first[i];
auto b = Polynomial(f.second.len - 1);
for (int i = 1; i <= f.second.len; i++)
b[i - 1] = f.second[i];
auto g = (a * b.inv(m + 1)).set(m);
for (int i = 0; i <= g.len; i++) {
g[i] = 1ll * g[i] * fac[i] % P;
}
for (int i = 1; i <= m; i++) {
printf("%d\n", g[i]);
}
return 0;
}