CF1061C Multiplicity

题目传送门

题目大意

从序列(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;
}

不要忘记点个赞哦

完结撒花

原文地址:https://www.cnblogs.com/LZY-LZY/p/12760581.html