[luogu p1069] 细胞分裂
细胞分裂
题目描述
\(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 个数 Si表示第 i 种细胞经过 1 秒钟可以分裂成同种细胞的个数。
输出格式
一个整数,表示从开始培养细胞到实验能够开始所经过的最少时间(单位为秒)。
如果无论\(Hanks\)博士选择哪种细胞都不能满足要求,则输出整数\(-1\)。
输入输出样例
输入样例 #1
1
2 1
3
输出样例 #1
-1
输入样例 #2
2
24 1
30 12
输出样例 #2
2
说明
【输入输出说明】
经过\(1\)秒钟,细胞分裂成\(3\)个,经过\(2\)秒钟,细胞分裂成\(9\)个,......,可以看出无论怎么分裂,细胞的个数都是奇数,因此永远不能分入\(2\)个试管。
【输入输出样例\(2\)说明】
第\(1\)种细胞最早在\(3\)秒后才能均分入\(24\)个试管,而第\(2\)种最早在\(2\)秒后就可以均分(每试管\(144/(241)=6\)个)。故实验最早可以在\(2\)秒后开始。
【数据范围】
对于 50%的数据,有\(m_1^{m_2} ≤ 30000\)。
对于所有的数据,有\(1 ≤N≤ 10000,1 ≤m_1 ≤ 30000,1 ≤m_2 ≤ 10000,1 ≤ S_i ≤ 2,000,000,000\)。
NOIP 2009 普及组 第三题
分析
此题 \(M\) 非常大,因此如果你暴力满分我直接吃屏。观察一下题面,很显然能看出是一道数论。
先抛弃最小值不谈,来对单独一个细胞进行分析。假设这个细胞分裂能力是 \(s\),那么我们要求的就是满足 \(s ^ t | M\) 中 \(t\) 的最小值。
首先先将 \(s\) 和 \(M\) 质因数分解。(\(m_1\) 中若含有 \(P\) 个质因数 \(p\),那么 \(M = m_1 ^ {m_2}\) 中就有 \(m_2P\) 个质因数 \(p\),因此把 \(m_1\) 质因数分解,然后对每个质因数数量乘上 \(m_2\) 即可。这也正是为什么 \(M\) 总能表示为 \(m_1 ^ {m_2}\) 的情形。)
设 \(p\) 为 \(s\) 和 \(M\) 的一个共同质因数,\(s\) 中包含 \(s_p\) 个 \(p\),\(M\) 中包含 \(M_p\) 个 \(p\),那么若想让 \(s ^ t | M\),就必须满足 \(t \ge \left\lceil\dfrac{M_p}{s_p}\right\rceil\)。这样,枚举 \(p\),找到 \(\left\lceil\dfrac{M_p}{s_p}\right\rceil\) 的最大值,那么 \(t\) 值自然就是这个最大值了。
什么时候无解呢?
对于一个质数 \(p\),定义 \(s_p\) 为 \(s\) 中 \(p\) 因数的数量,\(M_p\) 为 \(M\) 中 \(p\) 因数的数量。若存在一个质数 \(p\),使得 \(s_p = 0\) 且 \(M_p \ne 0\),那么不存在 \(t\) 使得 \(s ^ t | M\)。
最后,对每个 \(s\) 都进行上次的操作,在得到的 \(t\) 值中找到最小值即可。
如果所有都是无解的,那么答案自然就是 \(-1\) 咯。
上代码:
代码
/*
* @Author: crab-in-the-northeast
* @Date: 2020-10-02 20:06:20
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2020-10-02 21:52:59
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <climits>
const int maxm1 = 30005;
int prime_num[maxm1], cnt;
bool isprime[maxm1];
void prime(int n) {
std :: memset(isprime, true, sizeof(isprime));
isprime[1] = false;
for (int i = 2; i <= n; ++i) {
if (isprime[i]) prime_num[++cnt] = i;
for (int j = 1; j <= cnt && i * prime_num[j] <= n; ++j) {
isprime[i * prime_num[j]] = false;
if (i % prime_num[j] == 0) break;
}
}
}
int tube_prime[maxm1], cell_prime[maxm1];
int main() {
int n, m1, m2, ans = INT_MAX;
std :: scanf("%d", &n);
std :: scanf("%d %d", &m1, &m2);
prime(30000);
for (int i = 1, div = m1; div != 1; ++i) {
while (div % prime_num[i] == 0) {
tube_prime[i] += m2;
div /= prime_num[i];
}
}
for (int i = 1; i <= n; ++i) {
int s, need = 0;
std :: scanf("%d", &s);
std :: memset(cell_prime, 0, sizeof(cell_prime));
for (int j = 1, div = s; j <= cnt; ++j) {
while (div % prime_num[j] == 0) {
++cell_prime[j];
div /= prime_num[j];
}
}
bool valid = true;
for (int j = 1; j <= cnt; ++j) {
if (tube_prime[j] != 0 && cell_prime[j] == 0) {
valid = false;
break;
}
if (tube_prime[j] != 0) {
int t = tube_prime[j] / cell_prime[j] + (tube_prime[j] % cell_prime[j] ? 1 : 0);
if (t > need) need = t;
}
}
if (valid && need < ans) ans = need;
}
if (ans == INT_MAX) puts("-1");
else std :: printf("%d\n", ans);
return 0;
}