Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) D. Power Products

题目大意

给出一个长度为 n n n的数组 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an
问有多少对 ( i , j ) , i < j \pod{i,j},i<j (i,j),i<j满足 a i × a j = x k a_i\times a_j= x^k ai×aj=xk x x x为任意整数。

时间限制

2s

数据范围

n ≤ 1 0 5 n\le 10^5 n105
a i ≤ 1 0 5 a_i\le 10^5 ai105

题解

涉及到乘法,就应该考虑质因数,不妨先分解质因数。
如果要使得 a i × a j = x k a_i\times a_j= x^k ai×aj=xk,则它们质因子的次幂均为 k k k的倍数。
于是就可以有这样应该结论 x k + 1 x^k+1 xk+1 x 1 x^1 x1这此题中是等价的,
于是先可以分解质因数,然后先处理一下质因子的次幂,均对 k k k取模。

现在所有的数的次幂都不超过 k k k,那么现在要找到与它相乘满足条件的数,那么显然就是在质因子的次幂做文章。

而因为 a i a_i ai不大,可以用一个桶来储存。

Code

//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <queue>
#define G getchar
#define ll long long
using namespace std;

ll read()
{
    char ch;
    for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
    ll n = 0 , w;
    if (ch == '-')
    {
        w = -1;
        ch = G();
    } else w = 1;
    for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
    return n * w;
}

const int N = 200003;
ll n , ans , K , tmp;
ll pri[N] , k[N] , m;
int prime[66] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317};
int ss[16] = {2 , 3 , 5 , 7 , 11 ,13 , 17 , 19 , 23 , 29 , 325 , 9375 , 28187 , 450775 , 9870504 , 1795265022};
int s[N];

ll mul (ll a , ll b , ll p) //a*b%p
{
    /*ll s = 0;
    for ( ; b ; )
    {
        if (b & 1) s = (s + a) % p;
        a = (a + a) % p;
        b = b >> 1;
    }
    return s;*/
    ll s = a * b - (ll)((long double) a * b / p +0.5) * p;
    return s < 0 ? s + p : s;
}

ll ksm (ll x , ll y , ll p)
{
    ll s = 1;
    for ( ; y ; )
    {
        if (y & 1) s = mul(s , x , p);
        x = mul (x , x , p);
        y = y >> 1;
    }
    return s;
}

bool MR_detect (ll n , ll a)
{
    if (n == a) return 1;
    if (a % n == 0) return 1;
    if (ksm(a , n - 1 , n) != 1) return 0;
    ll p = n - 1 , lst = 1;
    for ( ; ((p & 1) ^ 1) && lst == 1 ; )
    {
        p = p >> 1;
        lst = ksm(a , p , n);
        if (lst != 1 && lst != n - 1) return 0;
    }
    return 1;
}

bool MR(ll  n)
{
    if (n < 2) return 0;
    for (int i = 0 ; i < 13 ; i ++)
        if (! MR_detect(n , ss[i])) return 0;
    return 1;
}

ll F(ll x , ll C , ll p)
{
    return (mul(x , x , p) + C) % p;
}

ll gcd(ll a , ll b)
{
    return a % b ? gcd(b , a % b) : b;
}

ll Rand()
{
    return (ll)rand() + ((ll)rand() << 15) + ((ll)rand() << 30) + ((ll)rand() << 45);
}

ll PR(ll n)
{
    if (MR(n)) return n;
    if (n == 4) return 2;
    for ( ; ; )
    {
        ll C = Rand() % (n - 1) + 1;
        ll p1 = 0 , p2 = 0;
        ll s = 1 , tmp;
        for ( ; ; )
        {
            for (int i = 0 ; i < 128 ; i++)
            {
                p2 = F(F(p2 , C , n) , C , n);
                p1 = F(p1 , C , n);
                tmp = mul(s , abs(p1 - p2) , n);
                if (tmp == 0 || p1 == p2) break;
                s = tmp;
            }
            tmp = gcd(n , s);
            if (tmp > 1) return tmp;
            if (p1 == p2) break;
        }
    }
}

ll ksm1(ll x , int y)
{
    ll s = 1;
    for ( ; y && s < N && x < N ; )
    {
        if (y & 1) s = s * x;
        x = x * x;
        y = y >> 1;
    }
    //if ((x >= N && y > 0) || s > N ) return N ; else return s;
    return s;
}

void work(ll n)
{
    m = 0;

    for (int i = 0 ; i < 66 ; i++)
    {
        if (n % prime[i] == 0)
        {
            m++;
            tmp = prime[i];
            pri[m] = tmp;
            k[m] = 0;
            for ( ; n % tmp == 0 ; )
            {
                k[m]++;
                n = n / tmp;
            }
        }
    }

    if (n > 1)
    {
        m++;
        k[m] = 1;
        pri[m] = n;
    }

    /*for ( ; n != 1 ; )
    {
        ll tmp = PR(n);
        for ( ; !MR(tmp) ; )tmp = PR(tmp);
        m++;
        pri[m] = tmp;
        k[m] = 0;
        for ( ; n % tmp == 0 ; )
        {
            k[m]++;
            n = n / tmp;
        }
    }*/

    ll tmp = 1;
    n = 1;
    for (int i = 1 ; i <= m ; i++)
    {
        k[i] = k[i] % K;
        if (k[i] == 0) continue;
        for (int j = 1 ; j <= k[i] ; j++)
            n = n * pri[i];
        for (int j = 1 ; j <= K - k[i] ; j++)
        {
            tmp = tmp * pri[i];
            if (tmp > 100000) break;
        }
        //n = n * ksm1(pri[i] , k[i]);
        //if (tmp < N) tmp = tmp * ksm1(pri[i] , K - k[i]);
    }

    if (tmp < N) ans = ans + s[tmp];
    s[n]++;
}

int main()
{
    //freopen("h.in","r",stdin);
    //freopen("2.txt","w",stdout);

    srand((unsigned)3);

    n = read();
    K = read();
    ans = 0;
    for (int i = 1 ; i <= n ; i++)
    {
        tmp = read();
        work(tmp);
    }
    printf("%lld\n", ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值