[ABC132F] Small Products 题解
题意
一句话题意不用再翻译了吧。
思路
先考虑朴素的 dp
,设 \(dp_{i,j}\) 表示长度为 \(i\) 结尾数字为 \(j\) 的序列的方案数,状态很好转移:
\[dp_{i,j}=\sum_{a=1}^{\lfloor \frac{N}{j} \rfloor}dp_{i-1,a}
\]
这样时间复杂度是 \(\Theta(nk)\) 的,显然过不了。
考虑优化这个 dp
。
我们发现 \(\lfloor \frac{N}{j} \rfloor\) 在一段区间内的值是一样的,自然想到整除分块。
我们预处理出每一块的 \(\lfloor \frac{N}{j} \rfloor\) 值 \(m_i\)、长度 \(len_i\) 和块的个数 \(cnt\),这时的状态转移如下:
\[dp_{i,j}=\sum_{j=1}^{cnt}dp_{i,j-1}+dp_{i-1,cnt-j+1}\times len_{j}
\]
这里状态中的 \(j\) 表示第 \(j\) 块。
时间复杂度 \(\Theta(\sqrt{n}k)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,m;
int dp[105][100005];
int a[100005],len[100005],cnt;
signed main(){
scanf("%lld%lld",&n,&m);
for(int l=1,r;l<=n;l=r+1){
r=(n/(n/l));
a[++cnt]=r;
len[cnt]=r-l+1;
}
for(int i=1;i<=cnt;i++) dp[0][i]=1;
for(int i=1;i<=m;i++){
int d=cnt;
for(int j=1;j<=cnt;j++){
dp[i][j]=(dp[i][j-1]+len[j]*dp[i-1][d--]%mod)%mod;
}
}
printf("%lld",dp[m][cnt]);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步