然后你发现转移的式子它其实可以看做是卷起来。
把 fi+1,j 放到 a2j+v 的位置,pk 放到 bm+1−k 的位置。
然后 a 和 b 的卷积的第 i 位结果就是 fi,i−(m−1) 了。
然后你用任意模数 NTT 搞即可,这里用了 MTT 的方法。
然后复杂度就是 mlogmlogn 了。
代码
普通版(m2logn)
#include<cstdio>#define ll long long#define mo 1000000009usingnamespace std;
int n, m;
ll jc[61], inv[61], p[61];
ll f[21][61];
ll C(int n, int m){
if (n < m) return0;
if (m < 0) return0;
return jc[n] * inv[m] % mo * inv[n - m] % mo;
}
ll ksm(ll x, ll y){
ll re = 1;
while (y) {
if (y & 1) re = re * x % mo;
x = x * x % mo;
y >>= 1;
}
return re;
}
voidinit(){
jc[0] = 1;
for (int i = 1; i <= m + 1; i++) jc[i] = jc[i - 1] * i % mo;
inv[m + 1] = ksm(jc[m + 1], mo - 2);
for (int i = m; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mo;
int on = (m + 1) / 2, jn = (m + 2) / 2;
for (int k = 0; k <= m + 1; k++) {
for (int i = 0; i <= on; i += 2) {
p[k] = (p[k] + C(on, i) * C(jn, k - i) % mo) % mo;
}
}
}
intmain(){
scanf("%d %d", &n, &m);
init();
f[20][0] = 1; int need = n - m;
for (int i = 19; i >= 0; i--) {
int hv = (need >> i) & 1;
for (int j = 0; j <= m + 1; j++)
for (int k = 0; k <= m + 1; k++) {
int to = 2 * j + hv - k;
if (to < 0 || to > m + 1) continue;
f[i][to] = (f[i][to] + f[i + 1][j] * p[k] % mo) % mo;
}
}
ll ans = 1;
for (int i = n - m + 1; i <= n; i++) ans = ans * i % mo;
ans = ans * inv[m];
printf("%lld", (ans - f[0][0] + mo) % mo);
return0;
}
MTT版(mlogmlogn)
#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>#define lim 32000#define ll long long#define mo 1000000009usingnamespace std;
structcomplex {
double x, y;
complex (double xx = 0, double yy = 0) {
x = xx; y = yy;
}
};
int n, m;
ll jc[61], inv[61], p[61];
ll f[21][61], ans[201], a[201], b[201];
double Pi = acos(-1.0);
complex operator +(complex x, complex y) {
return (complex){x.x + y.x, x.y + y.y};
}
complex operator -(complex x, complex y) {
return (complex){x.x - y.x, x.y - y.y};
}
complex operator *(complex x, complex y) {
return (complex){x.x * y.x - x.y * y.y, x.x * y.y + x.y * y.x};
}
ll C(int n, int m){
if (n < m) return0;
if (m < 0) return0;
return jc[n] * inv[m] % mo * inv[n - m] % mo;
}
ll ksm(ll x, ll y){
ll re = 1;
while (y) {
if (y & 1) re = re * x % mo;
x = x * x % mo;
y >>= 1;
}
return re;
}
voidinit(){
jc[0] = 1;
for (int i = 1; i <= m + 1; i++) jc[i] = jc[i - 1] * i % mo;
inv[m + 1] = ksm(jc[m + 1], mo - 2);
for (int i = m; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mo;
int on = (m + 1) / 2, jn = (m + 2) / 2;
for (int k = 0; k <= m + 1; k++) {
for (int i = 0; i <= on; i += 2) {
p[k] = (p[k] + C(on, i) * C(jn, k - i) % mo) % mo;
}
}
}
structMTT_work {
complex p1[201 << 2], p2[201 << 2], q[201 << 2];
int limit, l_size, an[201 << 2];
voidFFT(complex *now, int op){
for (int i = 0; i < limit; i++)
if (i < an[i]) swap(now[i], now[an[i]]);
for (int mid = 1; mid < limit; mid <<= 1) {
complex Wn(cos(Pi / mid), op * sin(Pi / mid));
for (int R = (mid << 1), j = 0; j < limit; j += R) {
complex w(1, 0);
for (int k = 0; k < mid; k++, w = w * Wn) {
complex x = now[j + k], y = w * now[j + mid + k];
now[j + k] = x + y; now[j + mid + k] = x - y;
}
}
}
}
voidmul(int n, ll *x, int m, ll *y){
limit = 1; l_size = 0;
while (limit <= n + m) {
limit <<= 1; l_size++;
}
for (int i = 0; i < limit; i++) p1[i] = p2[i] = q[i] = (complex){0, 0};
for (int i = 0; i <= n; i++)
p1[i] = (complex){x[i] / lim, x[i] % lim}, p2[i] = (complex){x[i] / lim, -x[i] % lim};
for (int i = 0; i <= m; i++)
q[i] = (complex){y[i] / lim, y[i] % lim};
for (int i = 0; i < limit; i++)
an[i] = (an[i >> 1] >> 1) | ((i & 1) << (l_size - 1));
FFT(p1, 1); FFT(p2, 1); FFT(q, 1);
for (int i = 0; i < limit; i++) q[i].x /= limit, q[i].y /= limit;
for (int i = 0; i < limit; i++) p1[i] = p1[i] * q[i], p2[i] = p2[i] * q[i];
FFT(p1, -1); FFT(p2, -1);
for (int i = 0; i <= n + m; i++) {
ll a1b1 = (ll)floor((p1[i].x + p2[i].x) / 2 + 0.5) % mo;
ll a1b2 = (ll)floor((p1[i].y + p2[i].y) / 2 + 0.5) % mo;
ll a2b1 = ((ll)floor(p1[i].y + 0.5) - a1b2) % mo;
ll a2b2 = ((ll)floor(p2[i].x + 0.5) - a1b1) % mo;
ans[i] = (a1b1 * lim * lim + (a1b2 + a2b1) * lim + a2b2) % mo;
ans[i] = (ans[i] + mo) % mo;
}
}
}MTT;
intmain(){
scanf("%d %d", &n, &m);
init();
f[20][0] = 1; int need = n - m;
for (int i = 19; i >= 0; i--) {
int hv = (need >> i) & 1;
// for (int j = 0; j <= m + 1; j++)// for (int k = 0; k <= m + 1; k++) {// int to = 2 * j + hv - k;// if (to < 0 || to > m + 1) continue;// f[i][to] = (f[i][to] + f[i + 1][j] * p[k] % mo) % mo; // }memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b));
for (int j = 0; j <= m + 1; j++)//两倍加上进位 a[2 * j + hv] = f[i + 1][j];
for (int j = 0; j <= m + 1; j++)//反过来全部右移 m+1 b[m + 1 - j] = p[j];
MTT.mul((m + 1) * 2 + hv, a, m + 1, b);
for (int j = m + 1; j <= m + 1 + m + 1; j++)
f[i][j - (m + 1)] = ans[j];//因为 b 右移了所以要移回来 m+1 位 }
ll answ = 1;
for (int i = n - m + 1; i <= n; i++) answ = answ * i % mo;
answ = answ * inv[m];
printf("%lld", (answ - f[0][0] + mo) % mo);
return0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现