CF1061C Multiplicity

题目传送门

题目大意

从序列\(a_1,a_2,\ldots,a_n\)中选出非空子序列\(b_1,b_2,\ldots,b_k\),一个子序列合法需要满足\(∀i\in[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}\times[a_i%j==0] \]

但是这样时间复杂度就是\(O(n\cdot 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(n\sqrt{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;
}

不要忘记点个赞哦

完结撒花

posted @ 2020-04-23 14:28  AFewSuns  阅读(143)  评论(4编辑  收藏  举报