题解 - P5973

· · 题解

P5973 [PA2013] Iloczyn の 题目传送门。

题目简化

RT,题目足够简单。

思路讲解

黄至绿,DFS。

刚开始是想用 DP 做的,不会推方程……
然后发现 STD 就是 DFS……

首先可以看到 n 的范围是 110^9,再计算一下 20! \thickapprox 2 \times 10^{18},经过二分可以快速得到在 k = 13 时,已经无法拆分 n(13! \thickapprox 6 \times 10^9)

为了更加速度,我们还可以推算出来,只有在 k \le n 时,才可以拆分,可以自证,不进行详解。

容易知道,希望划分的 k 个数一定是 n 的约数,所以应先求其约数。

代码实现

我们知道极限数据是 4 \times 10^{12},如果是直接使用从 1n 的循环会 T 飞掉。
但容易知道,除平方数以外,所有自然数都具有偶数个因数,并且平均分布在 \sqrt n 两侧。可以反证:若某一侧比另一侧含有更多的因数,那么将无法成功配对,将有某两个因数的乘积大于或小于 n
这样就可以求约数了,\sqrt {10^9} \thickapprox 3 \times 10^4

CODE

fors(i, 1, i <= sqrt(n), 1) 
    if (!(n%i)) {
        a.push_back(i);
        if (i*i != n) a.push_back(n/i);
    }
sort(a.begin(), a.end());

求完约数就可以写 dfs 了。

约定:

$p$ 代表还需要除以 $p$ 才能将 $n$ 除尽。 $m$ 代表正在考虑第 $m$ 个约数。 首先从 $k, n, 0$ 开始。 终止条件:$d = 0$。 然后判断剩余的约数能否被取走。 因为在前面排了序,所以接下来的一定是比后面更小的,如果前面小的都除不尽的话就说明大的也没戏。 ### _CODE_ ```cpp int l = d, u = 1; fors(i, m, i<a.size() && l--, 1) if ((u*=a[i]) > p) return 0; ``` 接下来挨个找可以被整除的约数。 $u$ 代表的是接下来 $d$ 个约数的积。 ### _CODE_ ```cpp fors(i, m, i<a.size() && && i+d-1<=a.size() && u<=p, 1) { if (i != m) u = u/a[i-1]*a[i];// 如果不是第一个且 a[i-1] 不符合要求,就代表接下来还需要 a[i]。 if (!(p%a[i]) && dfs(d-1, p/a[i], i+1)) return 1;// 如果 p 能整除 a[i] 且 a[i] 配对成功的话就说明可行,直接真值返回。 } ``` # E.N.D.