题目大意
从序列(a_1,a_2,ldots,a_n)中选出非空子序列(b_1,b_2,ldots,b_k),一个子序列合法需要满足(∀iin[1,k],i|b_i)。求有多少互不相等的合法子序列,答案对(10^9+7)取模。
思路
一看到题,就会想起(dp),然后根据这类题的套路,就是设(f_{i,j})为(a)序列的前(i)个数中长度为(j)的合法子序列有多少个。不难想到转移方程:
[f_{i,j}=f_{i-1,j}+f_{i-1,j-1} imes[a_i%j==0]
]
但是这样时间复杂度就是(O(ncdot max(a_i))),严重超时,所以考虑优化。
因为第一维的(i)只和(i-1)有关,而且(j)要是(a_i)的因数,所以可以预处理出所有(a_i)的因数,第二维只用遍历(a_i)的因数就可以了。于是我们定义(f_i)为造了长度恰好为(i)的⼦序列的⽅案数(跟(a)序列的长度无关),(ys_{i,j})为(a_i)的第(j)个因数,则转移方程如下:
[f_{ys_{i,j}}=f_{ys_{i,j}}+f_{ys_{i,j}-1}(ys数组记录a_i的因数)
]
这样子时间复杂度就是(O(nsqrt{a_i}{})),就可以过了。是不是很简单?
代码
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
vector<int> ys[100001];
int n,a[100001],f[1000001],ans=0;
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=sqrt(a[i]);j++){
if(a[i]%j==0){
ys[i].push_back(j);
if(j*j!=a[i]) ys[i].push_back(a[i]/j);
}
}
}
f[0]=1;
for(int i=1;i<=n;i++) {
for(int j=ys[i].size()-1;j>=0;j--){
f[ys[i][j]]=(f[ys[i][j]]+f[ys[i][j]-1])%mod;
ans=(ans+f[ys[i][j]-1])%mod;
}
}
printf("%d",ans);
return 0;
}