洛谷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;
}