Hanks 博士是 BT(Bio-Tech,生物技术)领域的知名专家。现在,他正在为一个细胞实验做准备工作:培养细胞样本。
Hanks 博士手里现在有 NN 种细胞,编号从 1 \sim N1∼N,一个第 ii 种细胞经过 11 秒钟可以分裂为 S_iSi 个同种细胞(S_iSi 为正整数)。现在他需要选取某种细胞的一个放进培养皿,让其自由分裂,进行培养。一段时间以后,再把培养皿中的所有细胞平均分入 MM 个试管,形成 MM 份样本,用于实验。Hanks 博士的试管数 MM 很大,普通的计算机的基本数据类型无法存储这样大的 MM 值,但万幸的是,MM 总可以表示为 m_1m1 的 m_2m2 次方,即 M = m_1^{m_2}M=m1m2,其中 m_1,m_2m1,m2 均为基本数据类型可以存储的正整数。
注意,整个实验过程中不允许分割单个细胞,比如某个时刻若培养皿中有 44 个细胞,Hanks 博士可以把它们分入 22 个试管,每试管内 22 个,然后开始实验。但如果培养皿中有 55 个细胞,博士就无法将它们均分入 22 个试管。此时,博士就只能等待一段时间,让细胞们继续分裂,使得其个数可以均分,或是干脆改换另一种细胞培养。
为了能让实验尽早开始,Hanks 博士在选定一种细胞开始培养后,总是在得到的细胞“刚好可以平均分入 MM 个试管”时停止细胞培养并开始实验。现在博士希望知道,选择哪种细胞培养,可以使得实验的开始时间最早。
输入格式
第一行,有一个正整数 NN,代表细胞种数。
第二行,有两个正整数 m_1,m_2m1,m2,以一个空格隔开,即表示试管的总数 M = m_1^{m_2}M=m1m2。
第三行有 NN 个正整数,第 ii 个数 S_iSi 表示第 ii 种细胞经过 11 秒钟可以分裂成同种细胞的个数。
输出格式
一个整数,表示从开始培养细胞到实验能够开始所经过的最少时间(单位为秒)。
如果无论 Hanks 博士选择哪种细胞都不能满足要求,则输出整数 -1−1。
输入数据 1
1
2 1
3
Copy
输出数据 1
-1
Copy
输入数据 2
2
24 1
30 12
Copy
输出数据 2
2
Copy
提示
【输入输出样例 #1 说明】
经过 11 秒钟,细胞分裂成 33 个,经过 22 秒钟,细胞分裂成 99个,……,可以看出无论怎么分裂,细胞的个数都是奇数,因此永远不能分入 22 个试管。
【输入输出样例 #2 说明】
第 11 种细胞最早在 33 秒后才能均分入 2424 个试管,而第 22 种最早在 22 秒后就可以均分(每试管 144 / {24}^1 = 6144/241=6 个)。故实验最早可以在 22 秒后开始。
【数据范围】
对于 50 \%50% 的数据,有 m_1^{m_2} \le 30000m1m2≤30000。
对于所有的数据,有 1 \le N \le 100001≤N≤10000,1 \le m_1 \le 300001≤m1≤30000,1 \le m_2 \le 100001≤m2≤10000,1 \le S_i \le 2 \times {10}^91≤Si≤2×109。
NOIP 2009 普及组 第三题
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int read() //读入优化
{
char ch=getchar();
int a=0,x=1;
while(ch<'0'||ch>'9')
{
if(ch=='-') x=-x;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
a=(a<<3)+(a<<1)+(ch-'0');
ch=getchar();
}
return a*x;
}
int n,m1,m2,minx,gcdd,m,s,q,t,flag,tot,ans,tots,totq; //t代表q能分解成多少个s
int S[10001];
int gcd(int a,int b) //欧几里得算法求最大公约数
{
if(b==0) return a;
else return gcd(b,a%b);
}
int main()
{
n=read();
m1=read();
m2=read();
minx=1e9;
if(m1==1) //这里一定要注意特判m1==1的情况,不然会TLE
{
cout<<0; //无需等待,因为任何数都是1的倍数
return 0;
}
for(int i=1;i<=n;i++) S[i]=read();
for(int i=1;i<=n;i++)
{
tot=0;
m=m1; //拷贝一份m1的值
s=S[i]; //拷贝一份S[i]的值
flag=1; //判断是否有解
t=0; //记录最大公约数要减几个m2
while(m!=1)
{
gcdd=gcd(m,s); //第一小步,求最大公约数
if(gcdd==1) {flag=0;break;} //如果互质,那么肯定无解,直接跳出
m/=gcdd; //左边剩下了m/gcdd
q=s/gcdd; //q就是s除以gcdd后剩下的数
s=gcdd; //右边剩下了gcdd
t++; //每进入一次循环,就要多减1个m2
}
if(flag)
{
int gc=gcd(q,s); //先求出gcd(q,s)
if(gc!=1&&gc!=s) //单独讨论第三种情况
{
totq=0;tots=0; //注意清空
while(q%gc==0) //如果q中含有gc就分离
{
totq++; //q中含有gc的个数
q/=gc;
}
while(s%gc==0) //如果s中含有gc就分离
{
tots++; //s中含有gc的个数
s/=gc;
}
if((t*m2*tots+totq*(t-1)*m2)%(tots+totq)==0) //这种情况的公式,注意向上取整
ans=(t*m2*tots+totq*(t-1)*m2)/(tots+totq);
else ans=(t*m2*tots+totq*(t-1)*m2)/(tots+totq)+1;
minx=min(minx,ans);
}
else //第一,二种情况
{
while(q%s==0) //如果q里面含有s,分离出来
{
tot++; //tot表示能分离出来多少个s,第一种情况就是tot=0的情况
q/=s;
}
if((t*m2+tot*(t-1)*m2)%(tot+1)==0) //我不知道为什么ceil出来的答案不对,只能自己模拟向上取整了
ans=(t*m2+tot*(t-1)*m2)/(tot+1); //没余数说明正好整除
else ans=(t*m2+tot*(t-1)*m2)/(tot+1)+1;//如果有余数就要+1(这个1就是余数除成小数后再向上取整后得到的)
minx=min(minx,ans); //题目要求整体最小时间
}
}
}
if(minx==1e9) cout<<-1; //如果minx没被更新,说明无解
else cout<<minx;
return 0;
}