合适数对

合适数对

给定一个长度为 $n$ 的正整数数列 $a_{1},a_{2}, \dots ,a_{n}$ 和一个正整数 $k$。

请你判断共有多少个数对 $\left( {l,r} \right)$ 同时满足:

  • $1 \leq l < r \leq n$
  • 存在一个整数 $x$ 使得 $a_{l} \times a_{r} = x^{k}$ 成立

输入格式

第一行包含两个整数 $n,k$。

第二行包含 $n$ 个正整数 $a_{1},a_{2}, \dots ,a_{n}$。

输出格式

一个整数,表示满足条件的数对的数量。

数据范围

前三个测试点满足 $2 \leq n \leq 10$。
所有测试点满足 $2 \leq n \leq {10}^{5},2 \leq k \leq 100,1 \leq a_{i} \leq {10}^{5}$。

输入样例:

6 3
1 3 9 8 24 1

输出样例:

5

 

解题思路

  这题需要用到算术基本定理。对于一个数$N = P_{1}^{\alpha_{1}} \cdot P_{2}^{\alpha_{2}} \cdot \cdots \cdot P_{n}^{\alpha_{n}}$,如果$N$是$k$次幂的话,那么就有$k \mid \alpha_{i}, \left( {i=1,2,\dots,n} \right)$,否则就至少存在一个$\alpha_{i}$,有$k \nmid \alpha_{i}$。因此这道题目就是要任意找两个数$a_{i}$和$a_{j}$,使他们的乘积$a_{i} \times a_{j}$的所有质因子的次幂都满足$k \mid \alpha_{i}$,问有多少对这种数。

  我们枚举$j$,看$j$前面有多少个$a_{i}$使得$a_{i} \times a_{j}$满足条件。先把$a_{j}$进行质因数分解,得到$a_{j} = P_{1}^{\alpha_{1}} \cdot P_{2}^{\alpha_{2}} \cdot \cdots \cdot P_{n}^{\alpha_{n}}$,由于我们只关心质因数的次数是否为$k$的倍数,因此我们只需要关心质因数的次数模$k$后是否等于$0$。对于要找的$a_{i} = P_{1}^{\beta_{1}} \cdot P_{2}^{\beta_{2}} \cdot \cdots \cdot P_{n}^{\beta_{n}}$,应该满足$k \mid \left( \alpha_{i} + \beta_{i} \right)$(这是对应质因子的次数相加,如果具有不同的质因子,那么另一个数对应的次幂应该等于$0$,同样满足)。

  由于每一个$a_{i} \leq {10}^{5}$,我们可以估算一下一个数可以最多分解到多少个不同的质因数,有$2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 > {10}^{5}$,因此可以发现每个数最多可以分解到$6$个不同的质因子。因此每个数最多只有$6$个质因子的次幂需要补充为$k$的倍数。

  对于$a_{j}$,我们找到它分解质因数后所有次幂不是$k$的倍数的质因子,假设为$P_{i}$,假设这些质因子的次幂模$k$后为$r_{i}$,容易发现$1 \leq r_{i} < k$。对于$a_{i}$,其质因数分解后与$a_{j}$相对应的$P_{i}$的次幂模$k$后为$t_{i}$,如果对于所有的$r_{i} + t_{i} = k$都满足,那么这个就是我们要找的$a_{i}$。补充:因为$1 \leq r_{i} < k, 1 \leq t_{i} < k$,$r_{i} + t_{i} < 2 \cdot k$,因此小于$2 \cdot k$且能被$k$整除的数就只有$k$。

  我们可以直接用次幂模$k$后不为$0$的质因子的乘积进行哈希,例如上面的$P_{1}^{r_{1}} \cdot P_{2}^{r_{2}} \cdot \cdots \cdot P_{n}^{r_{n}}$,$P_{1}^{t_{1}} \cdot P_{2}^{t_{2}} \cdot \cdots \cdot P_{n}^{t_{n}}$。

  AC代码如下:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 typedef long long LL;
 6 
 7 const int N = 1e5 + 10;
 8 
 9 int cnt[N];
10 
11 LL power(int a, int b) {
12     LL ret = 1;
13     while (b--) {
14         ret *= a;
15         if (ret >= N) return 0; // 由于ai都满足<=1e5,因此大于1e5就没有意义了
16     }
17     return ret;
18 }
19 
20 int main() {
21     int n, k;
22     scanf("%d %d", &n, &k);
23     
24     LL ret = 0;
25     while (n--) {
26         int val;
27         scanf("%d", &val);
28         
29         LL p = 1, q = 1;    // p表示对aj来说,次幂模k后不为0的质因子的乘积;q表示对于要找的ai,次幂互补的质因子的乘积
30         for (int i = 2; i <= val / i; i++) {    // 对aj分解质因数
31             int t = 0;
32             while (val % i == 0) {
33                 t = (t + 1) % k;
34                 val /= i;
35             }
36             
37             if (t) {    // 次幂不为0才需要相乘
38                 p *= power(i, t);       // i^t,累乘的结果必然不超过1e5
39                 q *= power(i, k - t);   // 相同质因子的次幂要互补,即i^(k-t),累乘的结果可能超过1e5
40                 if (q >= N) q = 0;      // 超过1e5就没有意义了,赋值为0
41             }
42         }
43         if (val > 1) {
44             p *= val;
45             q *= power(val, k - 1);
46             if (q >= N) q = 0;
47         }
48         
49         ret += cnt[q];
50         cnt[p]++;
51     }
52     
53     printf("%lld", ret);
54     
55     return 0;
56 }

  上面的质因数分解的时间复杂度为$O \left( n \cdot \sqrt[]{n} \right)$。我们还可以用线性筛,因为线性筛可以得到每一个数的最小质因子,因此在分解质质因数时可以用最小质因子来进行分解,每一个数包含的质因子个数大约为$log~n$个,因此这种方法的时间复杂度为$O \left( n \cdot log~n \right)$的。

  AC代码如下:

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 typedef long long LL;
 6 
 7 const int N = 1e5 + 10;
 8 
 9 int primes[N], cnt, minp[N];
10 bool vis[N];
11 int mp[N];
12 
13 void get_prime(int n) {
14     for (int i = 2; i <= n; i++) {
15         if (!vis[i]) {
16             primes[cnt++] = i;
17             minp[i] = i;
18         }
19         for (int j = 0; primes[j] <= n / i; j++) {
20             vis[primes[j] * i] = true;
21             minp[primes[j] * i] = primes[j];
22             if (i % primes[j] == 0) break;
23         }
24     }
25 }
26 
27 LL power(int a, int b) {
28     LL ret = 1;
29     while (b--) {
30         ret *= a;
31         if (ret >= N) return 0;
32     }
33     return ret;
34 }
35 
36 int main() {
37     int n, k;
38     scanf("%d %d", &n, &k);
39     
40     get_prime(N);
41     
42     LL ret = 0;
43     while (n--) {
44         int val;
45         scanf("%d", &val);
46         
47         LL p = 1, q = 1;
48         while (val > 1) {
49             int t = minp[val], s = 0;
50             while (val % t == 0) {
51                 s = (s + 1) % k;
52                 val /= t;
53             }
54             
55             p *= power(t, s);
56             if (s) q *= power(t, k - s);
57             if (q >= N) q = 0;
58         }
59         
60         ret += mp[q];
61         mp[p]++;
62     }
63     
64     printf("%lld", ret);
65     
66     return 0;
67 }

 

参考资料

  AcWing 4319. 合适数对(AcWing杯 - 周赛):https://www.acwing.com/video/3751/

posted @ 2022-03-27 12:15  onlyblues  阅读(74)  评论(0编辑  收藏  举报
Web Analytics