一个 \(\mathcal{O}(m^{\frac{3}{4}}\log m)\) 做法。
令 \(a_0 = 1\)。
对于倍数问题,考虑类似差分的思想,定义 \(b_i = \frac{a_i}{a_{i - 1}}(1\le i\le n)\),那么合法的 \(a\) 和 \(b\) 是双射的,就只需要考虑对 \(b\) 计数了。
考虑到因为有 \(\prod\limits_{i = 1}^n b_i\le m\),所以 \(\sum\limits_{i = 1}^n [b_i > 1]\le \lfloor\log_2 m\rfloor\),即 \(b_i\) 中大于 \(1\) 的个数不超过 \(\log_2 m\) 个。
那么就只需要考虑 \(b_i > 1\) 的方案,再考虑把这些值放进 \(b\) 中的方案数。
对于把这些值放进 \(b\) 中的方案数是比较好算的。
考虑选了 \(k\) 个 \(> 1\) 的数,方案实际上就是把这 \(k\) 个数放在 \(k\) 个位置上,其余的 \(n - k\) 个位置填成 \(1\),那么方案数就是 \(\binom{n}{k}\)。
考虑到 \(k\le \log_2 m\),所以 \(\binom{n}{k}\) 是可以直接 \(\mathcal{O}(k)\) 推的(\(\binom{n}{m}\times \frac{n - m}{m + 1} = \binom{n}{m + 1}\)),复杂度不用带 \(\mathcal{O}(n)\)。
接下来就是考虑求 \(b_i > 1\) 的方案数了。
那么要求乘积 \(\le m\),如果直接维护乘积 \(p\) 显然不太行。
此时考虑到因为有 \(xy\le m \iff x\le \lfloor\frac{m}{y}\rfloor\) 和 \(\lfloor\frac{\lfloor\frac{m}{x}\rfloor}{y}\rfloor = \lfloor\frac{m}{xy}\rfloor\),所以实际上只需要维护 \(\lfloor\frac{m}{p}\rfloor\) 的值。
这样的好处就很明显了,因为 \(\lfloor\frac{m}{p}\rfloor\) 只有 \(\mathcal{O}(\sqrt{m})\) 个值,明显需要求的位置数量变少了。
于是可以设 \(f_{q, i}\) 为 \(\lfloor\frac{m}{p}\rfloor = q\) 且已经选了 \(i\) 个数的方案数。
转移就考虑由 \(f_{q, i}\) 向 \(f_{\lfloor\frac{q}{w}\rfloor, i + 1}(1 < w \le q)\) 转移,即选了一个数 \(w\)。
初值就是 \(f_{m, 0} = 1\) 了,即一个不选。
发现这个转移其实也是可以用整除分块来优化的,这就不描述了。
时间复杂度分析同杜教筛一样,只是多带了个维护选的数量的 \(\lfloor\log_2 m\rfloor\) 项,时间复杂度 \(\mathcal{O}(m^{\frac{3}{4}}\log m)\)。
给出的代码实现是直接搜索的 \(b_i\not = 1\) 的方案,组合数的预处理也是 \(\mathcal{O}(n)\) 的,跑的还行,可以借助这份代码理解。
#include<bits/stdc++.h>
using ll = long long;
constexpr ll mod = 998244353;
inline ll qpow(ll a, ll b, ll v = 1) {
while (b)
b & 1 && ((v *= a) %= mod), b >>= 1, (a *= a) %= mod;
return v;
}
const int maxn = 2e5 + 10;
ll fac[maxn], ifac[maxn];
inline ll binom(int n, int m) {return fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int n, m;
ll ans;
void dfs(int c, int val, ll f) {
if (c > n)
return ;
(ans += f * binom(n, c)) %= mod;
for (int l = 2, r; l <= val; l = r + 1) {
r = val / (val / l);
dfs(c + 1, val / l, f * (r - l + 1) % mod);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = fac[0] = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2);
for (int i = n; i; i--) ifac[i - 1] = ifac[i] * i % mod;
dfs(0, m, 1);
printf("%lld\n", ans);
return 0;
}
标签:lfloor,Atcoder,le,frac,int,ll,rfloor,Sequences,ARC116C
From: https://www.cnblogs.com/rizynvu/p/18450537