没 有 名 字 没有名字 没有名字
正 解 部 分 \color{red}{正解部分} 正解部分
设 F [ i , j ] F[i, j] F[i,j] 表示 i i i 位置填 j j j 满足条件的方案数, 则 F [ i , j ] = ∑ k = 1 ⌊ M j ⌋ F [ i − 1 , k ] F[i, j] = \sum\limits_{k=1}^{\lfloor \frac{M}{j} \rfloor} F[i-1, k] F[i,j]=k=1∑⌊jM⌋F[i−1,k], 直接转移复杂度 O ( N M 2 ) O(NM^2) O(NM2), 不可过 .
观察到 ⌊ M j ⌋ \lfloor \frac{M}{j} \rfloor ⌊jM⌋ 的取值仅有 O ( M ) O(\sqrt{M}) O(M) 个, 且转移是前缀和的形式, 因此考虑使用 整除分块 和 前缀和 优化,
设 F ′ [ i , j ] F'[i, j] F′[i,j] 表示 i i i 位置填 整除分块 从大到小 第 j j j 种 值所对应的 分母 的方案数, g [ i , j ] g[i, j] g[i,j] 表示 对应 前缀和, 则
F ′ [ i , j ] = ( r [ j ] − l [ j ] + 1 ) × ∑ k = 1 v a l [ j ] F [ i − 1 , k ] = ( r [ j ] − l [ j ] + 1 ) × ∑ k = 1 M p [ M v a l [ j ] ] ] F ′ [ i − 1 , k ] = ( r [ j ] − l [ j ] + 1 ) × g [ i − 1 , M p [ M v a l [ j ] ] ] ] \begin{aligned} &\ \ \ \ F'[i, j] \\ & = (r[j]-l[j]+1)\times \sum\limits_{k=1}^{val[j]} F[i-1, k] \\ & = (r[j]-l[j]+1) \times \sum\limits_{k=1}^{Mp[\frac{M}{val[j]]}]} F'[i-1,k] \\ & = (r[j]-l[j]+1)\times g[i-1, Mp[\frac{M}{val[j]]}]\ ]\end{aligned} F′[i,j]=(r[j]−l[j]+1)×k=1∑val[j]F[i−1,k]=(r[j]−l[j]+1)×k=1∑Mp[val[j]]M]F′[i−1,k]=(r[j]−l[j]+1)×g[i−1,Mp[val[j]]M] ]
最后
a
n
s
=
g
[
N
,
c
n
t
]
ans = g[N, cnt]
ans=g[N,cnt], 使用 std::map<int, int>
时间复杂度
O
(
N
M
log
M
)
O(N \sqrt{M} \log M)
O(NMlogM) .
c n t cnt cnt 为取值的总个数, 整除分块的值从前往后单调不增 : M M/2 M/3 M/3 M/4 M/4 …
M p [ x ] Mp[x] Mp[x] 为 x x x 对应的块的编号 .
v a l [ i ] val[i] val[i] 表示编号为 i i i 的块对应的值 .
但是
M
p
[
x
]
Mp[x]
Mp[x] 直接使用 std::map<int,int>
储存会
T
L
E
TLE
TLE,
观察到在 从小到大 枚举
j
j
j 时,
⌊
M
v
a
l
[
j
]
⌋
=
⌊
M
M
l
[
j
]
⌋
\lfloor \frac{M}{val[j]} \rfloor = \lfloor \frac{M}{\frac{M}{l[j]}} \rfloor
⌊val[j]M⌋=⌊l[j]MM⌋ 的值是 单调不增 的, 且只会变化
M
\sqrt{M}
M 次, 因此可以使用指针维护 .
时间复杂度 O ( N M ) O(N \sqrt{M}) O(NM) .
实 现 部 分 \color{red}{实现部分} 实现部分
#include<bits/stdc++.h>
#define reg register
const int maxn = 1000005;
const int mod = 1e9 + 7;
int N;
int M;
int cnt;
int Mp[maxn];
int val[maxn];
int llim[maxn];
int rlim[maxn];
int F[102][maxn];
int g[102][maxn];
int main(){
scanf("%d%d", &N, &M);
for(reg int l = 1, r; l <= M; l = r+1){
r = M/(M/l), llim[++ cnt] = l, rlim[cnt] = r;
F[1][cnt] = r-l+1, val[cnt] = M/l;
g[1][cnt] = (g[1][cnt-1] + F[1][cnt]) % mod;
}
for(reg int i = 2; i <= N; i ++){
int t = cnt;
for(reg int j = 1; j <= cnt; j ++){
while(t >= 1 && M/(M/llim[j]) > val[t]) t --;
F[i][j] = (rlim[j]-llim[j]+1)*1ll*g[i-1][t] % mod;
g[i][j] = (g[i][j-1] + F[i][j]) % mod;
}
}
printf("%d\n", g[N][cnt]);
return 0;
}