合适数对(算术基本定理)

题意

给定一个长度为 \(n\) 的正整数数列 \(a_1,a_2,\dots ,a_n\) 和一个正整数 \(k\)

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

  • \(1 \leq l < r \leq n\)
  • 存在一个整数 \(x\) 使得 \(a_l \times a_r = x^k\) 成立

数据范围

\(2 \leq n \leq 10^5,2 \leq k \leq 100,1 \leq a_i \leq 10^5\)

思路

数对个数问题,常规套路就是枚举一端,另一端可以通过前面处理的信息直接算出。在这里,我们枚举\(a_r\),那么\(a_l\)如何求呢?

考虑算术基本定理。我们将\(a_r \times a_l\)分解质因数,每个质因子的次数必然是\(k\)的倍数。

因此,我们对\(a_r\)分解质因数,将所有质因数的次数对\(k\)取模。对于每一个次数为\(i (i \neq 0)\)的质因数\(p\),在\(a_l\)中,\(p\)的次数必须为\(k - p\)

那么统计\(a_l\)的个数呢?可以考虑哈希。在这里,哈希方式就是剔除所有次数为\(0\)的质因子,将其他各项相乘。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 100010;

int n;
ll k;
ll cnt[N];

ll qmi(ll a, ll b)
{
    ll res = 1;
    while(b --) {
        res *= a;
        if(res >= N) {
            res = 0;
            break;
        }
    }
    return res;
}

int main()
{
    scanf("%d%lld", &n, &k);
    ll ans = 0;
    for(int i = 1; i <= n; i ++) {
        ll x;
        scanf("%lld", &x);
        ll p = 1, con_p = 1;
        for(int j = 2; j <= x / j; j ++) {
            if(x % j == 0) {
                ll s = 0;
                while(x % j == 0) {
                    s ++;
                    x /= j;
                }
                s %= k;
                if(s) {
                    p *= qmi(j, s);
                    con_p *= qmi(j, k - s);
                }
            }
        }
        if(x > 1) {
            p *= qmi(x, 1);
            con_p *= qmi(x, k - 1);
        }
        if(con_p >= N) con_p = 0;
        ans += cnt[con_p];
        cnt[p] ++;
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2022-05-27 22:12  pbc的成长之路  阅读(87)  评论(0)    收藏  举报