题解:洛谷 P1069 细胞分裂

【题目来源】

洛谷: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

【核心思想】

  1. 问题分析:有 \(N\) 种细胞,第 \(i\) 种细胞每秒分裂为 \(S_i\) 个。试管总数 \(M = m_1^{m_2}\)。需要选择一种细胞,使其经过 \(t\) 秒后的细胞数量 \(S_i^t\) 能够被 \(M\) 整除,即 \(M \mid S_i^t\),求最小的 \(t\)。若所有细胞均无法满足,输出 \(-1\)。本质是质因数分解 + 指数倍数计算问题。

  2. 算法选择

    • 质因数分解:将 \(M = m_1^{m_2}\) 进行质因数分解,得到每个质因数 \(p_j\) 的指数 \(B_j = e_j \times m_2\),其中 \(e_j\)\(m_1\)\(p_j\) 的指数。
    • 对每个 \(S_i\) 分解:分解 \(S_i\),得到每个质因数 \(p_j\) 的指数 \(T_j\)
    • 判定与计算:若 \(S_i\) 缺少 \(M\) 的某个质因数(即 \(T_j = 0\)\(B_j > 0\)),则该细胞不可能满足条件。否则,对每个质因数 \(p_j\),需要 \(t \times T_j \ge B_j\),即 \(t \ge \lceil B_j / T_j \rceil\)。所有质因数中所需 \(t\) 的最大值即为该细胞的最小分裂时间。
    • 取最小值:在所有可行细胞中取最小时间,若无可行为 \(-1\)
  3. 关键步骤

    • 读入数据:读取 \(N\)\(m_1, m_2\),以及 \(N\)\(S_i\)
    • 预处理质数表:筛出 \(30000\) 以内的所有质数(因为 \(m_1 \le 30000\))。
    • 分解 \(m_1\):对 \(m_1\) 质因数分解,得到数组 \(E_j\)(质因数 \(p_j\) 的指数),然后计算 \(M\) 的质因数分解:\(B_j = E_j \times m_2\)
    • 处理每个 \(S_i\)
      • \(S_i\) 质因数分解,得到 \(T_j\)
      • 检查是否覆盖所有 \(B_j > 0\) 的质因数:若存在 \(B_j > 0\)\(T_j = 0\),则不可行。
      • 若可行,计算 cnt = max(ceil(B_j / T_j)),记录为该细胞需要的时间。
    • 取答案:所有可行细胞中的最小 cnt,若没有则输出 \(-1\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \cdot \pi(30000) + N \log S_i)\),其中 \(\pi(30000) \approx 3245\)\(N \le 10000\),完全可行。
    • 空间复杂度:\(O(\pi(30000))\)
  5. 质因数分解与指数倍数

    • 整除条件转化\(M \mid S_i^t\) 等价于 \(M\) 的每个质因数 \(p_j\) 的指数不超过 \(S_i^t\)\(p_j\) 的指数。由于 \(S_i^t\)\(p_j\) 的指数为 \(t \times \text{exp}_{p_j}(S_i)\),因此要求 \(t \times T_j \ge B_j\)
    • 可行性判定:若 \(S_i\) 不包含 \(M\) 的某个质因数,则无论如何增加 \(t\),该质因数指数始终为 0,无法满足条件。
    • 最小时间计算:对每个质因数独立计算所需的最小 \(t\),取最大值,因为所有质因数要求必须同时满足。
    • 适用场景:适用于“幂次整除”类问题,核心是利用质因数分解将指数运算转化为线性约束。

【算法标签】

普及+ #质数

【代码详解】

#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-09-01 09:25  团爸讲算法  阅读(7)  评论(0)    收藏  举报