1 条题解
-
0
自动搬运
来自洛谷,原作者为

NaCly_Fish
北海虽赊,扶摇可接。搬运于
2025-08-24 21:56:24,当前版本为作者最后更新于2019-03-30 13:45:22,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这是一篇搬运的题解。
原作者:https://www.luogu.org/space/show?uid=21423/HEOI2016] 求和 线性解法](https://blog.csdn.net/EI_Captain/article/details/87906472)
orz EI!!!
先来推式子:
$$ans=\sum\limits_{i=0}^n\sum\limits_{j=0}^iS(i,j)\times j! \times 2^j $$$$=\sum\limits_{i=0}^n\sum\limits_{j=0}^nS(i,j)\times j! \times 2^j $$$$=\sum\limits_{i=0}^n\sum\limits_{j=0}^n2^j\sum\limits_{k=0}^j(-1)^{j-k}k^i \binom{j}{k} $$$$=\sum\limits_{j=0}^n\sum\limits_{k=0}^j(-1)^{j-k} \binom{j}{k} 2^j[n+1]_k $$$$[n]_q=\sum\limits_{i=0}^{n-1}q^i\space\text{is }\color{#66CCFF}\text{q-analog} $$大部分人的推导在这里就结束了,因为这个式子可以卷积。
但是其实这个式子还可以不卷积进行计算。这个东西的要点就是我们要看清楚形如的形式是什么。
设数列的生成函数是,那么这个和式其实就是带入了。
反观我们的式子中:因此,我们得到的项的系数就由
分子可以用二项式定理化开,然后再除一个一次式,这是可以递推的。
至于把所有的
算出来为什么没有快速幂的,那是因为关于是完全积性函数,合数的是可以被筛出来的。这部分的复杂度就是
解法部分到这里就结束了。
下面是EI巨佬的代码,不要抄哦#include <cstdio> #include <cstring> #include <functional> #include <vector> #define LOG(FMT...) fprintf(stderr, FMT) using namespace std; typedef long long ll; const int P = 998244353; void exGcd(int a, int b, int& x, int& y) { if (!b) { x = 1; y = 0; return; } exGcd(b, a % b, y, x); y -= a / b * x; } int inv(int a) { int x, y; exGcd(a, P, x, y); if (x < 0) x += P; return x; } int mpow(int x, int k) { int ret = 1; while (k) { if (k & 1) ret = ret * (ll)x % P; x = x * (ll)x % P; k >>= 1; } return ret; } struct Simple { int n; vector<int> fac, ifac, inv; void build(int n) { this->n = n; fac.resize(n + 1); ifac.resize(n + 1); inv.resize(n + 1); fac[0] = 1; for (int x = 1; x <= n; ++x) fac[x] = fac[x - 1] * (ll)x % P; inv[1] = 1; for (int x = 2; x <= n; ++x) inv[x] = -(P / x) * (ll)inv[P % x] % P + P; ifac[0] = 1; for (int x = 1; x <= n; ++x) ifac[x] = ifac[x - 1] * (ll)inv[x] % P; } Simple() { build(1); } void check(int k) { int nn = n; if (k > nn) { while (k > nn) nn <<= 1; build(nn); } } int gfac(int k) { check(k); return fac[k]; } int gifac(int k) { check(k); return ifac[k]; } int ginv(int k) { check(k); return inv[k]; } int binom(int n, int m) { if (m < 0 || m > n) return 0; return gfac(n) * (ll)gifac(m) % P * gifac(n - m) % P; } } simp; const int N = 100010, PC = 30010; int n; int pc; int a[N]; bool vis[N]; int p[PC]; int pw[N]; void sieve() { pw[1] = 1; for (int x = 2; x <= n; ++x) { if (!vis[x]) { p[++pc] = x; pw[x] = mpow(x, n + 1); } for (int i = 1; x * p[i] <= n; ++i) { vis[x * p[i]] = true; pw[x * p[i]] = pw[x] * (ll)pw[p[i]] % P; if (x % p[i] == 0) break; } } } int main() { scanf("%d", &n); simp.check(n + 1); for (int i = 0; i <= n; ++i) a[i] = ((n + 1 - i) & 1) ? (P - simp.binom(n + 1, i)) : simp.binom(n + 1, i); int pw = mpow(2, n + 1); for (int i = 0; i <= n; ++i) a[i] = a[i] * (ll)pw % P; --a[0]; for (int i = 0; i <= n; ++i) a[i] = (P - a[i]) % P; int q = inv(3) * 2 % P; for (int i = 1; i <= n; ++i) a[i] = (a[i - 1] * (ll)q + a[i]) % P; int ans = (a[0] + a[1] * (ll)(n + 1)) % P; sieve(); for (int i = 2; i <= n; ++i) ans = (ans + simp.ginv(i - 1) * (::pw[i] - 1LL) % P * a[i]) % P; if (ans < 0) ans += P; ans = ans * (ll)inv(3) % P; printf("%d\n", ans); return 0; }ps:如果谁对这篇题解有更加易懂的解释,请在讨论区写出来qwq
- 1
信息
- ID
- 3047
- 时间
- 3000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者