Loading

P4091 [HEOI2016/TJOI2016]求和

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;
}
posted @ 2021-01-15 15:08  zzctommy  阅读(107)  评论(0编辑  收藏  举报