TUPCOUNT - Editorial

PROBLEM LINK:

Practice
Div1
Div2
Div3

Setter: Daanish Mahajan
Tester: Manan Grover
Editorialist: Ajit Sharma Kasturi

DIFFICULTY:

Easy-medium

PREREQUISITES:

Euler totient function

PROBLEM:

Given a positive integer N, we need to find the number of tuples (A,B,C,D) where 1 \leq A,B,C,D \leq N and {A} \cdot {B} = {C} \cdot {D}.

QUICK EXPLANATION:

  • This problem is equivalent to finding the number of tuples (A,B,C,D) where 1 \leq A,B,C,D \leq N and \frac{A} {B} = \frac{C}{D}.

  • The number of tuples (A,B,C,D) for which A=B and \frac{A}{B} = \frac{C}{D} will be N^2.

  • If we find the number of tuples (A,B,C,D) for which A\geq B,( let this be x ), we can also find the final answer which will be 2\cdot x - N^2.

  • Every fraction \frac{A}{B} can be reduced into a unique irreducible fraction \frac{X}{Y} i.e, GCD(X,Y)=1.

  • In order to solve the case for X \geq Y, we can iterate over every X from 1 to N and add \phi(X) \cdot \lfloor \frac{N}{X} \rfloor \cdot \lfloor \frac{N}{X} \rfloor to the answer where \phi is the euler totient function. This is because the number of Y for which GCD(X,Y)=1 is \phi(X) and each of the irreducible fraction \frac{X}{Y} contributes \lfloor \frac{N}{X} \rfloor \cdot \lfloor \frac{N}{X} \rfloor fraction pairs to the answer.

EXPLANATION:

Let us consider the equation A\cdot B = C \cdot D . This equation can be rewritten as follows: \frac{A}{C}=\frac{D}{B}. Therefore, we can reformulate the problem as follows: Given a positive integer N, we need to find the number of tuples (A,B,C,D) where 1 \leq A,B,C,D \leq N and \frac{A}{B} = \frac{C}{D}. The answer for this problem must be the same as the answer for our original problem.

The number of fractions \frac{A}{B} for which A=B will be N. They are \frac{1}{1}, \frac{2}{2}, \frac{3}{3}, \dots \frac{N}{N}. Therefore, the number of tuples (A,B,C,D) where A=B and \frac{A}{B} = \frac{C}{D} will be the number of pairs we can form with these N fractions which is N \cdot N.

Let us solve the case for A \geq B . Let this be x. Then, by symmetry, the number of tuples (A,B,C,D) for A \leq B will also be x. Therefore, our final answer will then be
x+x - ( number of tuples (A,B,C,D) where A=B and \frac{A}{B} = \frac{C}{D} )
= x+x-(N \cdot N)
=2 \cdot x-(N \cdot N).

Therefore, we could easily find the final answer if we have the answer for the case A \geq B.

The most crucial property of \frac{A}{B} = \frac{C}{D} is that both \frac{A}{B} and \frac{C}{D} can be reduced to a unique irreducible fraction \frac{X}{Y} i.e, GCD(X, Y) = 1.

Thus if we go through every irreducible fraction \frac{X}{Y} where X \geq Y and calculate the number of tuples (A,B,C,D) where \frac{A}{B} = \frac{C}{D} = \frac{X}{Y}, then we are done.

The number of fractions which are equal to \frac{X}{Y} is \lfloor \frac{N}{X} \rfloor. They are \frac{X}{Y}, \frac{2 \cdot X}{2 \cdot Y}, \frac{3 \cdot X}{3 \cdot Y}, \dots \frac{\lfloor \frac{N}{X} \rfloor \cdot X}{\lfloor \frac{N}{X} \rfloor \cdot Y} .

The number of pairs we can make from these fractions are \lfloor \frac{N}{X} \rfloor \cdot \lfloor \frac{N}{X} \rfloor .

Let us fix some X, and let tot be the number of possible Y where X \geq Y and gcd(X, Y)=1. Clearly, tot = \phi(X) where \phi is the euler-totient function. We can precompute the values of \phi(X) for all 1 \leq X \leq N.

The number of irreducible fractions \frac{X}{Y} where X \geq Y is \phi(X) and each of them contribute \lfloor \frac{N}{X} \rfloor \cdot \lfloor \frac{N}{X} \rfloor to the answer.

From this information we can calculate the answer for the case A \geq B as follows:
Iterate over X from 1 to N and add \phi(X) \cdot \lfloor \frac{N}{X} \rfloor \cdot \lfloor \frac{N}{X} \rfloor to the answer.

By calculating the answer for A \geq B, we can easily find the total number of tuples (A,B,C,D) for which \frac{A}{B} = \frac{C}{D} as explained above.

TIME COMPLEXITY:

O(N \log N) or O(N \log \log N) depending on the implementation for precomputation of \phi values.

SOLUTION:

Editorialist's solution
#include <bits/stdc++.h>
using namespace std;

int phi[1000005]; // Euler totient function phi

void precompute(int n)
{
     phi[1] = 1;

     for (int i = 2; i <= n; i++)
          phi[i] = i - 1;

     for (int i = 2; i <= n; i++)
     {
          //Based on the divisor sum property of phi
          for (int j = 2 * i; j <= n; j += i)
               phi[j] -= phi[i];
     }
}

int main()
{
     precompute(1000000);

     int tests;
     cin >> tests;

     while (tests--)
     {
          int n;
          cin >> n;

          long long int ans = 0;
          for (int i = 1; i <= n; i++)
          {
               ans += (1LL * phi[i] * (n / i) * (n / i));
          }

          ans = 2 * ans - 1LL * n * n;

          cout << ans << endl;
     }
     return 0;
}


Setter's solution

#include <bits/stdc++.h>
#define int long long
//#include <sys/resource.h>
#define initrand mt19937 mt_rand(time(0));
#define rand mt_rand()
#define MOD 1000000007
#define INF 1000000000
#define mid(l, u) ((l+u)/2)
#define rchild(i) (i*2 + 2)
#define lchild(i) (i*2 + 1)
#define mp(a, b) make_pair(a, b)
#define lz lazup(l, u, i);

using namespace std;

const int N = 1e6 + 5;
int phi[N];
signed main() {
    phi[0] = 0;
    phi[1] = 1;
    for (int i = 2; i < N; i++) phi[i] = i;
    for (int i = 2; i < N; i++) {
        if (phi[i] == i) {
            for (int j = i; j < N; j += i)
                phi[j] -= phi[j] / i;
        }
    }
    int t;
    cin>>t;
    assert(1<=t && t<=10);
    while(t--){
        int n;
        cin>>n;
        assert(1<=n && n<=1e6);
        int ans = n*n;
        for(int q = 2;q<=n;q++){
            int poss = (n/q);
            poss *= poss;
            poss *= phi[q]*2;
            ans += poss;
        }
        cout<<ans<<endl;
    }
    return 0;
}


Tester's solution
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 1000001
int main(){
  ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
  ll dp[N] = {};
  for(int i = 1; i < N; i++){
    dp[i] = 2 * i - dp[i];
    for(int j = 2 * i; j < N; j += i){
      dp[j] += dp[i];
    }
  } 
  int t;
  cin>>t;
  while(t--){
    ll n;
    cin>>n;
    ll ans = 0;
    for(int i = 1; i < n + 1; i++){
      ll k = n / i;
      ans += k * k * dp[i];
    }
    ans -= n * n;
    cout<<ans<<"\n";
  }
  return 0;
}

Please comment below if you have any questions, alternate solutions, or suggestions. :slight_smile:

14 Likes

Very nice problem ! I solved it in O(NloglogN) , but I think there is also an O(n) solution with some linear sieve.

1 Like

It seems like I have to learn number theroy.

4 Likes