题解:洛谷 P1069 [NOIP 2009 普及组] 细胞分裂

【题目来源】

洛谷:[P1069 NOIP 2009 普及组] 细胞分裂 - 洛谷

【题目描述】

Hanks 博士是 BT(Bio-Tech,生物技术)领域的知名专家。现在,他正在为一个细胞实验做准备工作:培养细胞样本。

Hanks 博士手里现在有 \(N\) 种细胞,编号从 \(1∼N\),一个第 \(i\) 种细胞经过 \(1\) 秒钟可以分裂为 \(S_i\) 个同种细胞(\(S_i\) 为正整数)。现在他需要选取某种细胞的一个放进培养皿,让其自由分裂,进行培养。一段时间以后,再把培养皿中的所有细胞平均分入 \(M\) 个试管,形成 \(M\) 份样本,用于实验。Hanks 博士的试管数 \(M\) 很大,普通的计算机的基本数据类型无法存储这样大的 \(M\) 值,但万幸的是,\(M\) 总可以表示为 \(m_1\)\(m_2\) 次方,即 \(M=m_1^{m_2}\),其中 \(m_1,m_2\) 均为基本数据类型可以存储的正整数。

注意,整个实验过程中不允许分割单个细胞,比如某个时刻若培养皿中有 \(4\) 个细胞,Hanks 博士可以把它们分入 \(2\) 个试管,每试管内 \(2\) 个,然后开始实验。但如果培养皿中有 \(5\) 个细胞,博士就无法将它们均分入 \(2\) 个试管。此时,博士就只能等待一段时间,让细胞们继续分裂,使得其个数可以均分,或是干脆改换另一种细胞培养。

为了能让实验尽早开始,Hanks 博士在选定一种细胞开始培养后,总是在得到的细胞“刚好可以平均分入 \(M\) 个试管”时停止细胞培养并开始实验。现在博士希望知道,选择哪种细胞培养,可以使得实验的开始时间最早。

【输入】

第一行,有一个正整数 \(N\),代表细胞种数。

第二行,有两个正整数 \(m_1,m_2\),以一个空格隔开,即表示试管的总数 \(M=m_1^{m_2}\)

第三行有 \(N\) 个正整数,第 \(i\) 个数 \(S_i\) 表示第 \(i\) 种细胞经过 \(1\) 秒钟可以分裂成同种细胞的个数。

【输出】

一个整数,表示从开始培养细胞到实验能够开始所经过的最少时间(单位为秒)。

如果无论 Hanks 博士选择哪种细胞都不能满足要求,则输出整数 \(−1\)

【输入样例】

1 
2 1 
3

【输出样例】

-1

【算法标签】

《洛谷 P1069 细胞分裂》 #数学# #素数判断,质数,筛法# #NOIP普及组# #2009#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 10005, M = 30005;  // 定义最大细胞数和最大质数范围

int n;                  // 细胞数量
int m_1, m_2;           // 试管参数m1和m2
int s[N];               // 每个细胞的编号
int p[M], len;          // 质数数组和质数个数
int B[M], T[M];         // B存储m1的质因数分解,T临时存储分解结果
int ans = -1;           // 最终答案,初始为-1

// 判断是否为质数
bool isPrime(int x)
{
    if (x == 1) return false;
    for (int i = 2; i <= sqrt(x); i++)
        if (x % i == 0) return false;
    return true;
}

// 质因数分解函数
void D(int x)
{
    memset(T, 0, sizeof(T));
    for (int i = 1; i <= len; i++)  // 遍历所有质数
    {
        while (x % p[i] == 0)       // 能被当前质数整除
        {
            T[i]++;                 // 对应质数的指数加1
            x /= p[i];              // 除掉这个质因数
        }
    }
    return;
}

int main()
{
    // 输入数据
    cin >> n >> m_1 >> m_2;
    for (int i = 1; i <= n; i++) 
        cin >> s[i];
    
    // 特判:如果m1=1,直接输出0
    if (m_1 == 1)
    {
        cout << 0 << endl;
        return 0;
    }
    
    // 预处理:筛出30000以内的所有质数
    for (int i = 2; i <= 30000; i++)
        if (isPrime(i)) 
            p[++len] = i;
    
    // 对m1进行质因数分解
    D(m_1);
    // 备份m1^m2的质因数分解结果
    for (int i = 1; i <= len; i++) 
        B[i] = T[i] * m_2;
    
    // 处理每个细胞
    for (int i = 1; i <= n; i++)
    {
        // 对当前细胞编号进行质因数分解
        D(s[i]);
        int cnt = -1;
        
        // 检查是否满足条件
        for (int j = 1; j <= len; j++)
        {
            if (B[j])  // 如果m1^m2有这个质因数
            {
                if (T[j] == 0)  // 但细胞没有这个质因数
                {
                    cnt = -1;    // 不满足条件
                    break;
                }
                else
                {
                    // 计算需要的倍数
                    int k = ceil(1.0 * B[j] / T[j]);
                    cnt = max(cnt, k);
                }
            }
        }
        
        // 更新答案
        if (cnt != -1)
        {
            if (ans == -1) 
                ans = cnt;
            else 
                ans = min(ans, cnt);
        }
    }
    
    // 输出结果
    cout << ans << endl;
    return 0;
}

【运行结果】

1 
2 1 
3
-1
posted @ 2026-02-18 20:14  团爸讲算法  阅读(3)  评论(0)    收藏  举报