洛谷P1069 细胞分裂

本来切了,却因为算错复杂度了没敢写。。。

题目传送门

思路:

其实思路很好想啊,就是分解质因数,然后逐个暴力判断就能过。如果这个数包含\(m1\)的所有质因子,那么肯定能凑出来;反之,如果有一个不包含,那么死也凑不出来。然后暴力枚举到底最少要几个能凑出来即可。这样一看,好像复杂度就不是很友好。首先把\(m1\)要分解质因数,将质因数开桶存下来。然后再一个个枚举\(a_i\),同时还要写一层循环来循环\(m1\)的质因子,在找到相同质因子时还要再来一个循环找\(a_i\)中有几个,从而得出答案。看着这复杂度就害怕。然而,就这复杂度竟然没问题。

让我们来理性思考一波,第一层循环是必不可少的,因为你不能不循环\(a_i\)然后我们看第二层循环,发现\(m1\)的质因子个数其实少之又少啊。\(m1\)\(2^{31}\)次方还要小,感性理解一下,就算每个质因子个数都只有一个,让种类尽可能多,也是不可能超过\(31\)个的。那也就是说,这层看似循环到了\(30000\)的循环,最多也不会循环\(31\)次。。。然后看内部的第三层暴力,发现是同样的道理,就算全是\(2\),这样能保证所有第三层循环的次数最大,但是也超不过\(31\)。同时,这样的话,第二层循环就只有\(1\)了。其实,第二层循环加上第三层循环,总共不会超过\(31\)次。。。

所以总时间复杂度并不是\(O(10000*30000*?)\),而是\(O(10000*31)\)。。。

代码实现其实非常简单,主要是算错复杂度的锅。。。要是考场上因为这没写出来就亏大了。

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<ctime>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,m1,m2,b[30010],a[10010],sum,ans=1000000000,t;
int main()
{
	n=read();m1=read();m2=read();
	t=m1;
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	for(int i=2;i<=m1;i++){
		while(m1%i==0&&m1>0){
			m1/=i;
			b[i]++;
		}
		b[i]*=m2;
	}//预处理
	for(int i=1;i<=n;i++){
		bool flag=1;
		int maxx=0;
		for(int j=2;j<=t;j++){
			if(b[j]){
				if(a[i]%j==0){
					int sum=0;
					while(a[i]>0&&a[i]%j==0){
						a[i]/=j;
						sum++;
					}
					if(sum>=b[j]){
						maxx=max(maxx,1);//如果本来就比m1的质因子个数多,那么只需分裂1次就行了
					}
					else{
						if(b[j]%sum==0){
							maxx=max(maxx,b[j]/sum);
						}
						else{
							maxx=max(maxx,b[j]/sum+1);//手玩一下找找规律即可
						}
					}
				}
				else{
					flag=0;//如果有一个质因子没有包含那也不行,所以直接break
					break;
				}
			}
		}
		if(flag){
			ans=min(ans,maxx);
		}
	}
	if(ans==1000000000){
		printf("-1\n");
	}
	else{
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2020-10-29 17:15  徐明拯  阅读(72)  评论(0编辑  收藏  举报