1 条题解
-
0
题意
定义动态规划数组 ,其中 表示从前 个元素 中选出大小为 的“好子序列”的方案数。最终答案为 。
思路
状态转移方程为:
$$dp[i][j] = \begin{cases} dp[i-1][j] + dp[i-1][j-1] & \text{如果 } a[i] \text{ 是 } j \text{ 的倍数} \\ dp[i-1][j] & \text{否则} \end{cases} $$直接维护二维 会超出内存限制,但注意到 只依赖于 ,因此可以用一维 数组滚动更新。此外, 只有在 是 的约数时才需要更新。我们可以用 的时间求出数 的所有约数。
算法步骤
- 初始化一维数组 ,长度为 ,,其余为 。
- 遍历每个元素 :
- 求出 的所有约数。
- 对于每个约数 (从大到小遍历,避免重复更新),执行 。
- 最终答案为 。
复杂度分析
- 时间复杂度:,其中 表示最大约数个数, 表示 的最大值。
- 也可以使用筛法预处理每个数的约数,时间复杂度为 $O(\text{maxA} \cdot \log(\text{maxA}) + n \cdot \text{maxD})$。
代码说明
- 使用一维数组 滚动更新,节省空间。
- 对于每个 ,枚举其所有约数,并按照从大到小的顺序更新 ,确保每个 只被当前元素更新一次。
- 最终累加 到 得到答案。
#include<iostream> #include<cmath> #include<algorithm> using namespace std; const int mod=1e9+7; long long n,a[1000010],f[1000010],ans,y[1000010]; int main(){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; f[0]=1;//使单独一个数能成为合法子序列 for(int i=1;i<=n;i++){ int top=0; for(int j=1;j<=sqrt(a[i]);j++){ if(a[i]%j)continue; y[++top]=j; if(j*j!=a[i])y[++top]=a[i]/j; }//求因数 sort(y+1,y+top+1);//排序 for(int j=top;j>=1;j--) f[y[j]]=(f[y[j]]+f[y[j]-1])%mod;//状态转移方程 } for(int i=1;i<=n;i++) ans=(ans+f[i])%mod; cout<<ans; return 0; }
- 1
信息
- ID
- 6716
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者