# 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编辑  收藏  举报