#P1002. [NOIP2009普及组] 细胞分裂

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
#include
#include
#include
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<

你可能感兴趣的:(算法,c++)