P4091 [HEOI2016/TJOI2016]求和
\[f(n)=\sum_{i=0}^{n}\sum_{j=0}^{i}S_2(i,j)2^j(j!)\\
=\sum_{j=0}^{n}2^jj!\sum_{i=0}^{n}S_2(i,j)\\
=\sum_{j=0}^{n}2^jj!\sum_{i=0}^{n}\dfrac{1}{j!}\sum_{k=0}^{j}(-1)^{j-k}\binom{j}{k}k^i\\
=\sum_{j=0}^{n}2^j\sum_{i=0}^{n}\sum_{k=0}^{j}(-1)^{j-k}\binom{j}{k}k^i\\
=\sum_{j=0}^{n}2^j\sum_{k=0}^{j}(-1)^{j-k}\binom{j}{k}\sum_{i=0}^{n}k^i\\
=\sum_{j=0}^{n}2^j\sum_{k=0}^{j}(-1)^{j-k}\binom{j}{k}\dfrac{1-k^{n+1}}{1-k}\\
=\sum_{j=0}^{n}2^jj!\sum_{k=0}^{j}\dfrac{(-1)^{j-k}}{(j-k)!}\dfrac{1-k^{n+1}}{(1-k)k!}
\]
水爆了,直接卷积就好了。等比数列记得判一下 \(k=1\) 就没有任何细节了。
说一下为啥第二步把 \(\sum\limits_{i=j}^{n}\) 换成 \(\sum\limits_{i=0}^{n}\) 。正确性显然,加了一堆 \(0\)。 毕竟少一个变量比多一个变量方便吧。我一开始也是写从 \(j\) 开始的,最后一步发现没法卷积,审视了一下式子发现改掉边界就可以卷积了。所以其实我并不是一开始就这么算的,当然应该有人运气好或者直觉好一开始就这么写就可以少推几分钟。
现在做出题都没有之前的兴奋感了,不知道是这题太水了还是我多项式做厌了。或许该停一阵子多项式?昨晚CF打炸了,根本没空开G这个数数题。其余部分感觉因为数数落下了,得去捡起来。
const int N = 100005;
const int M = N << 2;
#define mod 998244353
int n, A[M], B[M], ans;
namespace math{
int fac[N], ifc[N], inv[N];
inline int qpow(int n, int k) {
int res = 1;
for (; k; k >>= 1, n = 1ll * n * n % mod)
if (k & 1) res = 1ll * n * res % mod;
return res;
}
void initmath(const int &n = N - 1) {
fac[0] = 1; rep(i, 1, n) fac[i] = 1ll * fac[i - 1] * i % mod;
ifc[n] = qpow(fac[n], mod - 2); per(i, n - 1, 0) ifc[i] = 1ll * ifc[i + 1] * (i + 1) % mod;
inv[1] =1; rep(i, 2, n) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
}
}
using namespace math;
namespace poly{
int rev[M],lg,lim;
void init_poly(const int &n) {
for (lg = 0, lim = 1; lim < n; ++lg, lim <<= 1);
for (int i = 0; i < lim; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
}
void NTT(int *a, int op) {
for (int i = 0; i < lim; ++i)
if (i > rev[i]) swap(a[i], a[rev[i]]);
const int g = op ? 3 : inv[3];
for (int i = 1; i < lim; i <<= 1) {
const int wn = qpow(g, (mod - 1) / (i << 1));
for (int j = 0; j < lim; j += i << 1) {
int w0 = 1;
for (int k = 0; k < i; ++k, w0 = 1ll * w0 * wn % mod) {
const int X = a[j + k], Y = 1ll * w0 * a[i + j + k] % mod;
a[j + k] = (X + Y) % mod, a[i + j + k] = (X - Y + mod) % mod;
}
}
}
if (op) return; const int ilim = qpow(lim, mod - 2);
for (int i = 0; i < lim; ++i) a[i] = 1ll * a[i] * ilim % mod;
}
#define clr(a, n) memset(a, 0, sizeof(int) * (n))
#define cpy(a, b, n) memcpy(a, b, sizeof(int) * (n))
void poly_mul(int *f, int *g, int *ans, int n, int m) {
static int A[M], B[M]; init_poly(n + m);
cpy(A, f, n), clr(A + n, lim - n), NTT(A, 1);
cpy(B, g, m), clr(B + m, lim - m), NTT(B, 1);
for (int i = 0; i < lim; ++i) ans[i] = 1ll * A[i] * B[i] % mod;
NTT(ans, 0);
}
}
signed main(){
initmath();
n = read();
for (int i = 0; i <= n; ++i) A[i] = i & 1 ? mod - ifc[i] : ifc[i];
for (int i = 0; i <= n; ++i){
if (i == 1) B[i] = n + 1;
else B[i] = 1ll * (qpow(i, n + 1) - 1) * qpow(1ll * fac[i] * (i - 1) % mod, mod - 2) % mod;
}
poly::poly_mul(A, B, A, n + 1, n + 1);
for (int i = 0, j = 1; i <= n; ++i, j = j * 2 % mod) ans = (ans + 1ll * j * fac[i] % mod * A[i] % mod) % mod;
printf("%d\n", ans);
return 0;
}
路漫漫其修远兮,吾将上下而求索