觉得平时写博客的速度太慢了,今晚尝试一下 20 mins 完成一篇有质量的博客qwq
实际上用了 50 分钟qaq
洛谷入口 codeforces 入口
最近做的练习题中最简单的一道了。
结论:元素整除 − > -> −> 分解质因数
从序列 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,…,an 中选出非空子序列 b 1 , b 2 , … , b n b_1,b_2,\dots,b_n b1,b2,…,bn,一个子序列合法需要满足 ∀ i ∈ [ 1 , k ] , i ∣ b i ∀\ i\in[1,k],i\ |\ b_i ∀ i∈[1,k],i ∣ bi。求有多少互不相等的合法子序列,答案对 1 0 9 + 7 10^9+7 109+7 取模。
序列 1 , 1 { 1,1 } 1,1 有 2 2 2 种选法得到子序列 1 1 1 ,但 1 1 1 的来源不同,认为这两个子序列不相等。
首先,根据题意不难想到暴力做法:
f i , j = f i − 1 , j + [ a i % j = = 0 ] f i − 1 , j − 1 \large f_{i,j}=f_{i-1,j}+[a_i\%j==0]f_{i-1,j-1} fi,j=fi−1,j+[ai%j==0]fi−1,j−1
其中 f i , j f_{i,j} fi,j 表示前 i i i 个数中选出 j j j 个组成合法子序列的方案数。
时间复杂度是 O ( n 2 ) O(n^2) O(n2),肯定过不了。
分析上述方程,可以将其通过滚动数组将为一维↓↓
f j = f j + [ a i % j = = 0 ] f j − 1 \large f_j=f_j+[a_i\%j==0]f_{j-1} fj=fj+[ai%j==0]fj−1
注意为了不破坏答案正确性,应当将 j j j 从大到小更新。我起初 WA 原因
这时我们发现,当 a i % j ! = 0 a_i\%j\ !=0 ai%j !=0 时, f j f_j fj 保持不变。换句话说,只有当 a i % j = = 0 a_i\%j==0 ai%j==0 时, f i f_i fi 才会更新。
所以对于每个 a i a_i ai,我们可以先处理出 a i a_i ai 的质因子,并将其质因子排序,再进行状态转移,本题就可以过了。 实际上 20 mins 就写了以上这么点((
时间复杂度分析:
设 a i a_i ai 的因子数位 f a c i fac_i faci,那么时间复杂度为 O ( n ⋅ max i ∈ [ 1 , n ] f a c i log f a c i ) \large O(n\cdot \max\limits_{i\in[1,n]} fac_i\log fac_i) O(n⋅i∈[1,n]maxfacilogfaci),注意 log \log log 的存在在于需要将质因子排序。
实际上,当 a i ≤ 1 0 6 a_i\le 10^6 ai≤106 时, f a c i fac_i faci 的最大值为 240,最大时间复杂度为 O ( 1 0 5 ⋅ 240 ⋅ 8 ) = O ( 1.92 × 1 0 8 ) O(10^5\cdot 240\ \cdot 8)=O(1.92\times10^8) O(105⋅240 ⋅8)=O(1.92×108),3 s 时限可以过。
注意因为 a i a_i ai 质因子的最大值接近 1 0 6 10^6 106, f \large f f 数组要开 1 0 6 10^6 106 。我后来 RE 的原因
当然,将 a i a_i ai 质因子筛选,保证全部小于 n n n 再转移 f \large f f 也行。
C o d e Code Code
#include
using namespace std;
const int N=1e5+5,M=1e9+7;
int n,ans,b[N],f[N*10];
int main(){
scanf("%d",&n);
f[0]=1;
for(int i=1;i<=n;i++){
int a,c=0;
scanf("%d",&a);
for(int j=1;j*j<=a;j++){
if(a%j==0){
b[++c]=j;
if(j*j!=a) b[++c]=a/j;
}
}
sort(b+1,b+c+1);
for(int j=c;j>=1;j--) (f[b[j]]+=f[b[j]-1])%=M;
/*for(int j=c;j>=1;j--){ //Method 2 : ensure that all are less than N.
if(b[j]>n) continue;
(f[b[j]]+=f[b[j]-1])%=M;
}*/
}
for(int i=1;i<=n;i++) (ans+=f[i])%=M;
printf("%d",ans);
return 0;
}