CF1061C Multiplicity

21 篇文章 0 订阅
13 篇文章 0 订阅

觉得平时写博客的速度太慢了,今晚尝试一下 20 mins 完成一篇有质量的博客qwq

实际上用了 50 分钟qaq

CF1061C Multiplicity

洛谷入口 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=fi1,j+[ai%j==0]fi1,j1

其中 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]fj1

注意为了不破坏答案正确性,应当将 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(ni[1,n]maxfacilogfaci),注意 log ⁡ \log log 的存在在于需要将质因子排序。
实际上,当 a i ≤ 1 0 6 a_i\le 10^6 ai106 时, 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(105240 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<bits/stdc++.h>
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;
}

T h a n k s   f o r   r e a d i n g . Thanks\ for\ reading. Thanks for reading.


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值