bzoj 4555 求和 - 线性筛 - 生成函数

题目传送门

  传送门

题目大意

  (大家应该都知道)

$$
\begin {align}
f(n) &= \sum_{i = 0}^{n}\sum_{j = 0}^{i}\genfrac\{\}0{}{i}{j}j! 2^j \\
&= \sum_{i = 0}^{n}\sum_{j = 0}^{n}\genfrac\{\}0{}{i}{j}j! 2^j \\
&= \sum_{i = 0}^{n}\sum_{j = 0}^{n}\sum_{k = 0}^{j} k^i\binom{j}{k}(-1)^{j - k} 2^j \\
&= \sum_{j = 0}^{n}\sum_{k = 0}^{j} \binom{j}{k}(-1)^{j - k} 2^j \sum_{i = 0}^{n}k^i
\end {align}
$$

​  设$S_k(n) = \sum_{i = 0}^{n} k^i =\begin{cases}\frac{k^{n + 1} - 1}{k - 1}& (k\neq 1) \\ n + 1 &(k = 1)\end{cases} $

​  那么有$f(n) = \sum_{i = 0}^{n} \sum_{j = 0}^{i} \binom{i}{j}(-1)^{i - j} 2^i S_j(n + 1)$

​  注意到若$G(x) = \sum_{i = 0}^{n} g_i x^i$,那么$G(x + d) = \sum_{i = 0}^{n} \sum_{j = i}^{n} g_j \binom{j}{i} d^{j - i} x^i$
$$
\begin{align}
f(n) &= \sum_{j = 0}^{n} S_j(n) \sum_{i = j}^{n} \binom{i}{j} (-1)^{i - j} 2^{i}
\end{align}
$$
  那么这里的$G(x) = \sum_{i = 0}^{n} 2^i x^i, d = -1$
$$
\begin{align}
G(x) &= \frac{(2x)^{n + 1} - 1}{2x - 1} \\
G(x - 1) &= \frac{2^{n + 1} (x - 1)^{n + 1} - 1}{2x - 3}
\end{align}
$$
​  用二项式定理展开上面的,剩下暴力多项式除。

​  逆元和$k^n$都可以线性预处理。然后就做完了。

​Code

/**
 * bzoj
 * Problem#4555
 * Accepted
 * Time: 156ms
 * Memory: 3256k
 */
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

const int Mod = 998244353;

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;
		} 
};

Z<> qpow(Z<> a, int p) {
	Z<> rt = Z<>(1), pa = a;
	for ( ; p; p >>= 1, pa = pa * pa) {
		if (p & 1) {
			rt = rt * pa;
		}
	}
	return rt;
}

typedef Z<> Zi;

const int N = 1e5 + 5;

int n;
Zi Inv[N], _g[N], g[N];

void get_g() {
	Inv[0] = 0, Inv[1] = 1;
	for (int i = 2; i <= n + 1; i++) {
		Inv[i] = -Inv[Mod % i] * (Mod / i);
	}
	Zi C = 1, v = qpow(2, n + 1);
	for (int i = 0; i <= n + 1; i++) {
		_g[i] = v * C;
		if ((n + 1 - i) & 1) {
			_g[i] = -_g[i];
		}
		C = C * (n - i + 1) * Inv[i + 1];
	}
	_g[0] -= 1;
	// g = _g / (2x - 3)
	for (int i = n + 1; i; i--) {
		g[i - 1] = _g[i] * Inv[2];
		_g[i - 1] += g[i - 1] * 3;
	}
}

Zi pw[N];
int pri[N];
bitset<N> vis;
void Euler() {
	int num = 0;
	pw[0] = 0, pw[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!vis.test(i)) {
			pw[i] = qpow(i, n + 1);
			pri[num++] = i;
		}
		for (int *p = pri, *_p = pri + num, x; p != _p && (x = *p * i) <= n; p++) {
			vis.set(x);
			pw[x] = pw[i] * pw[*p];
			if (!(i % *p)) {
				break;
			}
		}
	}
}

int main() {
	scanf("%d", &n);
	get_g();
	Euler();
	Zi ans = g[0] + g[1] * (n + 1);
	for (int i = 2; i <= n; i++) {
		ans += (pw[i] - 1) * Inv[i - 1] * g[i];
	}
	printf("%d\n", ans.v);
	return 0;
}
posted @ 2020-04-15 15:08  阿波罗2003  阅读(177)  评论(0编辑  收藏  举报