洛谷P5907 数列求和加强版 / SPOJ MOON4
题面描述
求
对 取模的结果。
做法
为了推导方便,下令 。即求
我们裂项,即尝试寻找多项式 ,使得
即
答案将是 。
显然 。
这样临项点值的差,由于恒等式 ,可以考虑展开成下降幂来推导。具体地,设 ,则可以展开 式:
对比系数能够得到:
:
求一行斯特林数是 的,需要进一步优化。
直接求每一项系数也许有些困难,但是这里有一个取巧的办法:
如果我们知道 ,那么通过式,可以递推出 ,那么就可以利用这 个点值进行线性拉格朗日插值,避开了这个难处。
而 ,因此下面我们只需要求出 即可。省略这一步过程,我们恰好可以将这个系数的递推式求出来,即
这个式子是可以 求解的,参见 EI 的 Binomial Sum。由于我也是第一次学这个所以下面还是写一下过程。
下面令 ,求
写成生成函数就是
令 ,对于 来说,这本应是一个无穷级数,但利用 没有常数项的性质,我们可以将对于 的求和上限限制在 处(截断),之后的项都不会有贡献。因此就是求
那么 GF 写作:
这是一个关于 的有理分式,将分母移到一边就可以 求出 处系数,然后就做完了。退役OIer不想省略下面的细节,继续推导。我们设 ,那么
提取系数就是
最后,我们得到了,
这道题几乎做完了。剩下的就是特判 的情况,此时问题退化为自然数幂和,可以直接线性拉格朗日插值。
代码(代码中,使用 代替了 ):
#include <cstdio>
#define ll long long
const int M = 1e7, mod = 998244353;
inline int mul(int a, int b) {return (ll)a * b % mod;}
inline int add(int a, int b) {int ret = a + b; return ret >= mod ? ret - mod : ret;}
inline int minus(int a, int b) {int ret = a - b; return ret < 0 ? ret + mod : ret;}
inline int qpow(int a, int b) {
int ret = 1;
for(; b; b >>= 1, a = mul(a, a))
if(b & 1) ret = mul(ret, a);
return ret;
}
int f[M + 5], g[M + 5], p, n, m, ip, ip1;
int fac[M + 5], invf[M + 5];
int pre, suf[M + 5];
int cnt, v[M + 5], pr[M >> 2], pwm[M + 5];
inline int C(int m, int n) {return (ll)fac[m] * invf[n] % mod * invf[m - n] % mod;}
inline int pn1(int num, int p) {return p & 1 ? mod - num : num;}
int main() {
scanf("%d%d%d", &n, &ip, &m);
//initialization
fac[0] = 1;
p = qpow(ip, mod - 2);
for(int i = 1; i <= m + 1; ++i) fac[i] = mul(fac[i - 1], i);
invf[m + 1] = qpow(fac[m + 1], mod - 2);
for(int i = m; i >= 0; --i) invf[i] = mul(invf[i + 1], i + 1);
pwm[1] = 1;
for(int i = 2; i <= m + 2; ++i) {
if(!v[i]) v[i] = pr[++cnt] = i, pwm[i] = qpow(i, m);
for(int j = 1; j <= cnt; ++j) {
if(pr[j] > v[i] || i * pr[j] > m + 2) break;
v[i * pr[j]] = pr[j];
pwm[i * pr[j]] = mul(pwm[i], pwm[pr[j]]);
}
}
ip1 = qpow(p - 1, mod - 2);
if(p ^ 1) {
//calulate g, f
int t1 = qpow(ip1 + 1, mod - 2), t2 = qpow(ip1, m + 1);
// t1 = 1/t + 1, t2 = t ^ {m + 1}
g[0] = mul(t1, 1 + pn1(t2, m));
for(int i = 1; i <= m; ++i) {
g[i] = mul(t1, mul(ip1, g[i - 1]) + mul(C(m + 1, i), pn1(t2, m - i)));
f[0] = add(f[0], mul(pwm[i], g[i]));
}
f[0] = mul(ip1, f[0]);
}
if(p ^ 1) for(int i = 1; i <= m; ++i)
f[i] = minus(mul(p, f[i - 1]), pwm[i - 1]);
else for(int i = 1; i <= m + 1; ++i)
f[i] = add(f[i - 1], pwm[i]);
//prepare for lagruange interpolation
if(p == 1) --n, ++m;
pre = n + 1, suf[m + 1] = 1;
// for(int i = 1; i <= m; ++i) pre[i] = mul(pre[i - 1], (mod + n + 1 - i) % mod);
for(int i = m; i >= 1; --i) suf[i] = mul(suf[i + 1], minus(n + 1, i));
//calculate answer
int Ans = mul(f[0], pn1(mul(suf[1], invf[m]), m));
for(int i = 1; i <= m; ++i)
Ans = add(Ans, pn1(mul(f[i], mul(mul(pre, suf[i + 1]), mul(invf[i], invf[m - i]))), m - i)),
pre = mul(pre, minus(n + 1, i));
if(p ^ 1) Ans = minus(f[1], mul(Ans, qpow(qpow(p, n), mod - 2)));
printf("%d", Ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】