[CP] 质因子分解的优化
以 D. Divide and Equalize 为例,对于给定数组 \(a\),这道题定义了一种操作:任意选择数组的两个元素 \(a_i\) 和 \(a_j\),使得 \(a_i\) 变成 \(\frac{a_i}{x}\),同时 \(a_j\) 变成 \(a_j\cdot{}x\),其中 \(x\,|\,a_i\),你可以执行此操作任意次数,问能否使数组中所有的元素相等。
注意到这一操作并没有改变数组中所有元素的乘积(\(\prod\limits_{i=1}^{n}a_i\)),只是将一个数的因子转移到了另外一个数上。当我们注意到这一点时,问题也就基本解决了——只需要对所有元素进行质因子分解,最后判断每种质因子的数量能否被数组的元素数量 \(n\) 整除即可,如果能,这意味着我们可以将数组中某些元素多余的质因子转移到其它元素上,并最终使每个元素都具有相同的质因子(且每种质因子的数量也相同)。
于是我使用欧拉筛法,预先计算出数据范围(\(10^6\))以内的所有素数,然后对每个数组元素进行试除,分解出所有的因子,统计因子个数,这是我一开始写的代码(m 是 std::unordered_map<int, int>):
std::cin >> t;
idx = 0;
while (t != 1) {
while (t % primes[idx] != 0)
++idx;
t /= primes[idx];
++m[primes[idx]];
}
实际效果很不理想,虽然通过了题目,但几乎就要 TLE(1918ms,时限 2000ms),参考了 jiangly 31ms 的 submission,我发现他在筛法函数里面加了一个 minp 数组,并在后续的分解质因子过程中采用 minp[t] 直接得到了 t 的质因子!相比我的代码,这无疑能带来很大的性能提升,节省许多无意义的取余操作,而取余是非常耗时的事情。
于是我开始研究 minp 数组的作用,结合它的名称和代码,我发现它存储的是当前元素的最小质因子,对此只需要对筛法函数做很小的改动,因为欧拉筛法的性质天然地保证每个合数只被筛到一次,所以当我们确定某个合数不是素数(也就是将其筛去)时,就可以相应地在 minp 数组中记录,此时记录的质因子一定是最小的。
在添加了 minp 数组后,质因子分解部分也得以优化为:
while (t != 1) {
++m[minp[t]];
t /= minp[t];
}
这是优化前后的对比:


浙公网安备 33010602011771号