题解:洛谷 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
【核心思想】
-
问题分析:有 \(N\) 种细胞,第 \(i\) 种细胞每秒分裂为 \(S_i\) 个。试管总数 \(M = m_1^{m_2}\)。需要选择一种细胞,使其经过 \(t\) 秒后的细胞数量 \(S_i^t\) 能够被 \(M\) 整除,即 \(M \mid S_i^t\),求最小的 \(t\)。若所有细胞均无法满足,输出 \(-1\)。本质是质因数分解 + 指数倍数计算问题。
-
算法选择:
- 质因数分解:将 \(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\)。
-
关键步骤:
- 读入数据:读取 \(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\)。
-
时间/空间复杂度:
- 时间复杂度:\(O(N \cdot \pi(30000) + N \log S_i)\),其中 \(\pi(30000) \approx 3245\),\(N \le 10000\),完全可行。
- 空间复杂度:\(O(\pi(30000))\)。
-
质因数分解与指数倍数:
- 整除条件转化:\(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
浙公网安备 33010602011771号