题解:[EPXLQ2024 fall round] Simple Math Problem
[EPXLQ2024 fall round] Simple Math Problem 题解
题外话
出题人题解。
被比赛负责人爆标了,原本的标算还被卡了。
题意
对于一个正整数 \(n\),求有多少个 \(m \in (0,n)\),满足 \(nm\) 能被 \(n-m\) 整除。
Solution
下面所有时间复杂度中的 \(d\) 表示 \(d(n)\),即 \(n\) 的因数个数,\(V\) 表示答案;其它公式中的 \(d\) 表示 \(\gcd(m,n)\)。
Subtask 0
观察可得(后面会给详细证明),答案始终为 \(1\),输出即可。
时间复杂度:\(O(T)\)。
Subtask 1 ~ 2
枚举每个可能的 \(m\),每个验证即可。
时间复杂度:\(O(n)\)。
Subtask 3
先进行一些小小的推导。设 \(n = dx, m = dy, gcd(x, y) = 1\),则:
因此,我们要对于每个 \(d \mid n\) 找到所有 \(s \mid x\) 满足 $ s \in (0, {x \over d})\(,\) \operatorname{Card}( {x \in Z \mid x = ds} )$ 就是答案。
这也可以解释为什么 Subtask 0 的答案是 \(1\):只有 \(s=1\) 可行。
因此,我们可以考虑枚举每个 \(n\) 的可能因数 \(d\),再二分确定哪些 \(n\) 的因数能被选择,最后进行去重即可。记 \(s\) 为 \(n\) 的因数个数,时间复杂度与之有关。
时间复杂度:\(O(T \times (\sqrt n \times d^2))\)。
Subtask 4
注意到此时可能的因数只有 \(1,p,q,pq\)。考虑如何找到 \(p\)(这样即可找到 \(q\))。直接枚举 \(10^7\) 内的因数并判断即可。
事实上,如果 \(p \ne q\),这四个数都各自没有额外的因数,故答案就是 \(4\);如果 \(p=q\) 则答案为 \(2\)。
时间复杂度:\(O(T \sqrt p)\)。
Subtask 5
注意到因数个数不会很多,但 \(O(\sqrt n)\) 枚举因数的复杂度无法接受。由于 \(p \le 10^6\),可以通过质数筛先筛出 \(10^6\) 内的质数。
注意,此时我们不能再使用 \(O(d^2)\) 的做法计算。观察到 \(d,s\) 一定都是 \(n\) 的因数,因此如果 \(n\) 的质因数分解为 \(n = p_1^{q_1} \times p_2^{q_2} \times \dots \times p_s^{q_s}\),所有可能答案 \(x\) 的分解 \(x=p_1^{k_1} \times p_2^{k_2} \times \dots \times p_s^{k_s}\) 一定有 \(\forall i \in [1,s], k_i \le 2q_i\),同时还要满足 \(x < n\)。因此,我们可以考虑 DFS 搜索每个可能的 \((k_1,k_2,\dots,k_s)\) 并加以剪枝。由唯一分解定理可知,此时搜索到的 \(x\) 不会重复,每个搜索到的 \(x\) 均为答案。
接下来是对时间复杂度的估计。当 \(d(n)\) 较大且 \(p_1,p_2,\dots,p_s\) 都较小时,答案将较大,因为此时大量 \(p_i\) 的乘积仍然在 \([1,n]\) 范围内的概率增大(这也是测试点 \(2\) 数据的构造方式)。因此,可以通过构造数据得出,本题中可以认为 \(V \le 10^6\)。
时间复杂度:\(O(T \times (p + V))\)。
Subtask 6
从 Subtask 4 的做法出发。我们现在只需要质因数,因此我们枚举因数时只需要枚举 \(10^7\) 内的质因子,如果出现整除直接除到无法整除为止,可以证明取到的因子一定是质因子(因为合数全被除掉了)。
时间复杂度:\(O(T \times (\sqrt n + V))\)。事实上时间复杂度会比这个小很多,因为 \(\le 10^{14}\) 的数能产生的本质不同质因子的个数是有限的。
Code
//written by Naught
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 it;
#define Maxn 65
#define fo(i, l, r) for (ll i = l; i <= r; ++i)
inline int read(int x=0, bool f=0, char c=getchar()) {for(;!isdigit(c);c=getchar()) f^=!(c^45);for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);return f?-x:x;}
inline ll lread(ll x=0, bool f=0, char c=getchar()) {for(;!isdigit(c);c=getchar()) f^=!(c^45);for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);return f?-x:x;}
ll n, a[Maxn], b[Maxn];
int ans, sum;
void dfs(int cnt, ll num)
{
if(num > n) return ; // 剪枝
if(cnt > sum) return ++ans, void();
it s = num;
fo(i, 0, b[cnt])
{
if(s > n) break;
dfs(cnt + 1, (ll)s);
s *= a[cnt];
}
}
void Init(ll n)
{
sum = 0;
for(int i = 2; 1ll * i * i <= n; ++i) if(n % i == 0)
{
a[++sum] = i, b[sum] = 0;
while(n % i == 0) ++b[sum], n /= i;
}
if(n - 1) a[++sum] = n, b[sum] = 1;
fo(i, 1, sum) b[i] <<= 1;
}
void Solve()
{
ans = 0;
dfs(1, 1);
printf("%d\n", ans-1);
}
signed main()
{
int _ = read();
while(_--) n = lread(), Init(n), Solve();
return 0;
}
Tips
记得开 __int128。

浙公网安备 33010602011771号