Search code examples
c++arraysalgorithmgreatest-common-divisor

Codeforces: Two Divisors


Here is the question:
You are given n integers a1, a2, …, an.

For each ai find its two divisors d1>1 and d2>1 such that gcd(d1+d2,ai)=1 (where gcd(a,b) is the greatest common divisor of a and b) or say that there is no such pair.

Input:
The first line contains single integer n (1 ≤ n ≤ 5*10^5) — the size of the array a.

The second line contains n integers a1,a2,…,an (2 ≤ ai ≤ 10^7) — the array a.

Output:
To speed up the output, print two lines with n integers in each line.

The i-th integers in the first and second lines should be corresponding divisors d1>1 and d2>1 such that gcd(d1+d2,ai)=1 and −1 if there is no such pair. If there are multiple answers, print any of them.

Here is my solution:

#include <iostream>
#define ll long long
#define enter cout<<"\n"
using namespace std;

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    int n; cin>>n;
    int a[n] = {0};
    for(int i=0; i<n; i++) cin>>a[i];
    int d1[n] = {0}, d2[n] = {0};
    for(int i=0; i<n; i++){
        for(int j=2; j*j<=a[i]; j++){
            if(a[i]%j==0 && __gcd(j+a[i]/j, a[i])==1){
                d1[i] = j; d2[i] = a[i]/j; break;
            }
        }
        if(d1[i]==0){
            d1[i] = -1; d2[i] = -1;
        }
    }
    for(int i=0; i<n; i++){
        cout<<d1[i]<<" "; 
    } enter;
    for(int i=0; i<n; i++){
        cout<<d2[i]<<" ";
    } enter;
    return 0;
}

I couldn't find an algorithm more efficient than this. I am getting "Time limit exceeded" on a large case like n=500000. How can I make it more efficient? A little help is appreciated.


Solution

  • The most important thing we need to focus here is gcd(d1+d2 , a[i]) = 1.

    For this condition to be true, d1 and d2 should be coprimes.

    So, we need to find the 2 coprime divisors of a[i] and to find this, the smallest prime factor and it's multiplicity plays a big role.

    For eg:

    Suppose, N = p1^k1 * p2^k2 * ..... * pn^kn

    Here, pi = prime factor, ki = multiplicity of pi.

    60 = 2^2 * 3^1 * 5^1

    Smallest prime factor = p1 = 2

    Multiplicity of smallest prime factor = 2

    So, 2^2 = 4, is coprime with the other pi^ki, i.e., 3^1 and 5^1.

    Also, 2^2 = 4 is coprime with (3^1 * 5^1)

    This is because, p1^k1 is corprime with (p2^k2 * p3^k3 * ...... * pn^kn).

    So, Final Answer:

    d1 = p1^k1

    d2 = (p2^k2 * p3^k3 * ...... * pn^kn) = a[i]/d1

    Have a look at the following implementation which has Accepted verdict on Codeforces:

    #include <iostream>
    #include <vector>
    #include <cmath>
    
    #define maxn 1e7+10
    
    int smallestPrime[(int)maxn];
    
    std::vector<int> primes;
    
    void primeFactorization(){
    
        primes.push_back(2);
    
        smallestPrime[0] = smallestPrime[1] = 1;
        for ( int i = 2 ; i < maxn ; i += 2 )
            smallestPrime[i] = 2;
    
        for ( int i = 3 ; i <  maxn ; i += 2 ) {
            if ( smallestPrime[i] == 0 ){
                smallestPrime[i] = i;
                primes.push_back(i);
                for ( int j = i ; j*(long long)i <  maxn ; j += 2 ) {
                    if(smallestPrime[ i*j ] == 0)
                        smallestPrime[ i*j ] = i;
                }
            }
        }
    }
    
    int main(){
    
        primeFactorization();
    
        int n;
        std::cin>>n;
    
        int arr[1000000];
        for(int i=0;i<n;i++){
            std::cin>>arr[i];
        }
    
        int d1[1000000];
        int d2[1000000];
    
        for(int i=0;i<n;i++){
    
            int tmp = arr[i];
            int p1 = smallestPrime[tmp];
            int k1 = 0;
            while(tmp > 1 && p1 == smallestPrime[tmp])
                tmp /= smallestPrime[tmp],++k1;
    
            d1[i] = pow(p1, k1);
            
            d2[i] = arr[i]/d1[i];
    
            if(d1[i]==1 || d2[i]==1){
                d1[i]=-1;d2[i]=-1;
            }
    
        }
    
        for(int i=0;i<n;i++){
            std::cout<<d1[i]<<" ";
        }
    
        std::cout<<std::endl;
    
    
        for(int i=0;i<n;i++){
            std::cout<<d2[i]<<" ";
        }
    
        return 0;
    }
    

    Verdict:

    enter image description here