題目傳送門
直接考慮拆貢獻的組合意義差點搞自閉。
考慮一下朴素 dp 方程:
$$
f_{i, j} = f_{i - 1, j - 1} (j + a_i) +f_{i - 1, j }
$$
注意到這個式子當 $a_i = 0$ 的時候和第二類 Stirling 數的遞歸式比較像。
考慮令第二維為 $j' = i - j$,那么有:
$$
f_{i, j} = f_{i - 1, j} (i - j + a_i) +f_{i - 1, j - 1}
$$
考慮一下組合意義就是,每個元素要么不選,產生 $(a_i + i)$ 的貢獻,要么放進一個集合里,要么新開一個集合放進去,后者的貢獻為 $(-1)^{選擇元素個數 - 集合數量}$。
前面的分治 FFT 計算,后面多項式 exp 就行了。
Code
/**
* loj
* Problem#6703
* Accepted
* Time: 10433ms
* Memory: 11600k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;
#define ll long long
template <typename T>
void pfill(T* pst, const T* ped, T val) {
for ( ; pst != ped; *(pst++) = val);
}
template <typename T>
void pcopy(T* pst, const T* ped, T* pval) {
for ( ; pst != ped; *(pst++) = *(pval++));
}
const int N = 262144;
const int Mod = 998244353;
const int bzmax = 19;
const int g = 3;
void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
}
int inv(int a, int Mod) {
int x, y;
exgcd(a, Mod, x, y);
return (x < 0) ? (x + Mod) : (x);
}
template <const int Mod = :: Mod>
class Z {
public:
int v;
Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { }
friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~ (const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
};
typedef Z<> Zi;
Zi qpow(Zi a, int p) {
if (p < Mod - 1)
p += Mod - 1;
Zi rt = 1, pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
}
const Zi inv2 ((Mod + 1) >> 1);
class NTT {
private:
Zi gn[bzmax + 4], _gn[bzmax + 4];
public:
NTT() {
for (int i = 0; i <= bzmax; i++) {
gn[i] = qpow(Zi(g), (Mod - 1) >> i);
_gn[i] = qpow(Zi(g), -((Mod - 1) >> i));
}
}
void operator () (Zi* f, int len, int sgn) {
for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) {
if (i < j)
swap(f[i], f[j]);
for (k = len >> 1; k <= j; j -= k, k >>= 1);
}
Zi *wn = (sgn > 0) ? (gn + 1) : (_gn + 1), w, a, b;
for (int l = 2, hl; l <= len; l <<= 1, wn++) {
hl = l >> 1, w = 1;
for (int i = 0; i < len; i += l, w = 1) {
for (int j = 0; j < hl; j++, w *= *wn) {
a = f[i + j], b = f[i + j + hl] * w;
f[i + j] = a + b;
f[i + j + hl] = a - b;
}
}
}
if (sgn < 0) {
Zi invlen = ~Zi(len);
for (int i = 0; i < len; i++) {
f[i] *= invlen;
}
}
}
int correct_len(int len) {
int m = 1;
for ( ; m <= len; m <<= 1);
return m;
}
} NTT;
void pol_inverse(Zi* f, Zi* g, int n) {
static Zi A[N];
if (n == 1) {
g[0] = ~f[0];
} else {
int hn = (n + 1) >> 1, t = NTT.correct_len(n << 1 | 1);
pol_inverse(f, g, hn);
pcopy(A, A + n, f);
pfill(A + n, A + t, Zi(0));
pfill(g + hn, g + t, Zi(0));
NTT(A, t, 1);
NTT(g, t, 1);
for (int i = 0; i < t; i++) {
g[i] = g[i] * (Zi(2) - g[i] * A[i]);
}
NTT(g, t, -1);
pfill(g + n, g + t, Zi(0));
}
}
void pol_sqrt(Zi* f, Zi* g, int n) {
static Zi A[N], B[N];
if (n == 1) {
g[0] = f[0];
} else {
int hn = (n + 1) >> 1, t = NTT.correct_len(n + n);
pol_sqrt(f, g, hn);
pfill(g + hn, g + n, Zi(0));
for (int i = 0; i < hn; i++)
A[i] = g[i] + g[i];
pfill(A + hn, A + t, Zi(0));
pol_inverse(A, B, n);
pcopy(A, A + n, f);
pfill(A + n, A + t, Zi(0));
NTT(A, t, 1);
NTT(B, t, 1);
for (int i = 0; i < t; i++)
A[i] *= B[i];
NTT(A, t, -1);
for (int i = 0; i < n; i++)
g[i] = g[i] * inv2 + A[i];
}
}
typedef class Poly : public vector<Zi> {
public:
using vector<Zi>::vector;
Poly& fix(int sz) {
resize(sz);
return *this;
}
} Poly;
Poly operator + (Poly A, Poly B) {
int n = A.size(), m = B.size();
int t = max(n, m);
A.resize(t), B.resize(t);
for (int i = 0; i < t; i++) {
A[i] += B[i];
}
return A;
}
Poly operator - (Poly A, Poly B) {
int n = A.size(), m = B.size();
int t = max(n, m);
A.resize(t), B.resize(t);
for (int i = 0; i < t; i++) {
A[i] -= B[i];
}
return A;
}
Poly sqrt(Poly a) {
Poly rt (a.size());
pol_sqrt(a.data(), rt.data(), a.size());
return rt;
}
Poly operator * (Poly A, Poly B) {
int n = A.size(), m = B.size();
int k = NTT.correct_len(n + m - 1);
if (n < 20 || m < 20) {
Poly rt (n + m - 1);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
rt[i + j] += A[i] * B[j];
}
}
return rt;
}
A.resize(k), B.resize(k);
NTT(A.data(), k, 1);
NTT(B.data(), k, 1);
for (int i = 0; i < k; i++) {
A[i] *= B[i];
}
NTT(A.data(), k, -1);
A.resize(n + m - 1);
return A;
}
Poly operator ~ (Poly f) {
int n = f.size(), t = NTT.correct_len((n << 1) | 1);
Poly rt (t);
f.resize(t);
pol_inverse(f.data(), rt.data(), n);
rt.resize(n);
return rt;
}
Poly operator / (Poly A, Poly B) {
int n = A.size(), m = B.size();
if (n < m) {
return Poly {0};
}
int r = n - m + 1;
reverse(A.begin(), A.end());
reverse(B.begin(), B.end());
A.resize(r), B.resize(r);
A = A * ~B;
A.resize(r);
reverse(A.begin(), A.end());
return A;
}
Poly operator % (Poly A, Poly B) {
int n = A.size(), m = B.size();
if (n < m) {
return A;
}
if (m == 1) {
return Poly {0};
}
A = A - A / B * B;
A.resize(m - 1);
return A;
}
Zi Inv[N];
void init_inv(int n) {
Inv[0] = 0, Inv[1] = 1;
for (int i = 2; i <= n; i++) {
Inv[i] = Inv[Mod % i] * Zi((Mod - (Mod / i)));
}
}
void diff(Poly& f) {
if (f.size() == 1) {
f[0] = 0;
return;
}
for (int i = 1; i < (signed) f.size(); i++) {
f[i - 1] = f[i] * Zi(i);
}
f.resize(f.size() - 1);
}
void integ(Poly& f) {
f.resize(f.size() + 1);
for (int i = (signed) f.size() - 1; i; i--) {
f[i] = f[i - 1] * Inv[i];
}
f[0] = 0;
}
Poly ln(Poly f) {
int n = f.size();
Poly h = f;
diff(h);
f = h * ~f;
f.resize(n - 1);
integ(f);
return f;
}
void pol_exp(Poly& f, Poly& g, int n) {
Poly h;
if (n == 1) {
g.resize(1);
g[0] = 1;
} else {
int hn = (n + 1) >> 1;
pol_exp(f, g, hn);
h.resize(n), g.resize(n);
pcopy(h.data(), h.data() + n, f.data());
g = g * (Poly{1} - ln(g) + h);
g.resize(n);
}
}
Poly exp(Poly f) {
int n = f.size();
Poly rt;
pol_exp(f, rt, n);
return rt;
}
int n;
int a[N];
Poly dividing(int l, int r) {
if (l == r)
return Poly {1, Zi(a[l]) + l};
int mid = (l + r) >> 1;
return dividing(l, mid) * dividing(mid + 1, r);
}
Zi fac[N], _fac[N];
void prepare(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i;
_fac[n] = ~fac[n];
for (int i = n; i; i--)
_fac[i - 1] = _fac[i] * i;
}
int main() {
scanf("%d", &n);
prepare(n);
init_inv(n);
for (int i = 1; i <= n; i++)
scanf("%d", a + i);
Poly A = dividing(1, n);
Poly B (n + 1);
for (int i = 1; i <= n; i++)
B[i] = -_fac[i];
B = exp(B);
Zi ans = 0;
for (int i = 0; i <= n; i++) {
if ((n - i) & 1) {
ans -= A[i] * B[n - i] * fac[n - i];
} else {
ans += A[i] * B[n - i] * fac[n - i];
}
}
ans -= 1;
printf("%d\n", ans.v);
return 0;
}
