Problem:
Score : 1100 points

Problem Statement

Snuke got positive integers s_1,...,s_N from his mother, as a birthday present. There may be duplicate elements.

He will circle some of these N integers. Since he dislikes cubic numbers, he wants to ensure that if both s_i and s_j (i ≠ j) are circled, the product s_is_j is not cubic. For example, when s_1=1,s_2=1,s_3=2,s_4=4, it is not possible to circle both s_1 and s_2 at the same time. It is not possible to circle both s_3 and s_4 at the same time, either.

Find the maximum number of integers that Snuke can circle.

Constraints

1 ≦ N ≦ 10^5

1 ≦ s_i ≦ 10^{10}

All input values are integers.

Input

The input is given from Standard Input in the following format:

N
s_1
:
s_N

Output

Print the maximum number of integers that Snuke can circle.

Sample Input 1

8
1
2
3
4
5
6
7
8

Sample Output 1

6

Snuke can circle 1,2,3,5,6,7.

Sample Input 2

6
2
4
8
16
32
64

Sample Output 2

3

Sample Input 3

10
1
10
100
1000000007
10000000000
1000000009
999999999
999
999
999

Sample Output 3

9

Solution in C# (Mono 4.6.2.0) (judge verdict: Wrong Answer)

using System;
using System.Linq;//リストの使用
using System.Collections.Generic;
using System.Text;//テキストの高速出力に必要
class Program
{

	static void Main()
	{
		int n = int.Parse(Console.ReadLine());
		long[] nums = new long[n];
    for(int i = 0; i < n; i++) nums[i] = long.Parse(Console.ReadLine());
    long[][] numbers = new long[n][];
    long answer = 0;
    for(int i = 0; i < n; i++)
    {
      numbers[i] = new long[2];
      numbers[i][0] = nums[i];
      numbers[i][1] = 1;
    }

    List<long> primes = GetPrimeList(2500);

    List<long> numberList = new List<long>();
    List<long> numberPartnerList = new List<long>();
    List<long> numberCountList = new List<long>();

    for(int i = 0; i < n; i++)
    {
      for(int j = 0; j < primes.Count(); j++)
      {
        while(numbers[i][0] % (primes[j]*primes[j]*primes[j]) == 0)
        {
          numbers[i][0] /= (primes[j]*primes[j]*primes[j]);
          nums[i] /= primes[j]*primes[j]*primes[j];
        }

        if(numbers[i][0] % (primes[j]*primes[j]) == 0)
        {
          numbers[i][1] *= primes[j];
          nums[i] /= primes[j]*primes[j];
        }else if(numbers[i][0] % primes[j] == 0)
        {
          numbers[i][1] *= primes[j]*primes[j];
          nums[i] /= primes[j];
        }
      }
      if(nums[i] != 1) numbers[i][1] = -1;
    }

    Array.Sort(numbers, (a, b) => a[0].CompareTo(b[0]));

    for(int i = 0; i < n; i++)
    {
      if(numbers[i][1] == -1) answer++;
      else if(numberList.Count() == 0)
      {
        numberList.Add(numbers[i][0]);
        numberPartnerList.Add(numbers[i][1]);
        numberCountList.Add(1);
        //Console.WriteLine(numbers[i][0]+" "+numbers[i][1]);
      }else if(numberList[numberList.Count()-1] == numbers[i][0])
      {
        numberCountList[numberList.Count()-1]++;
      }else
      {
        numberList.Add(numbers[i][0]);
        numberPartnerList.Add(numbers[i][1]);
        numberCountList.Add(1);
        //Console.WriteLine(numbers[i][0]+" "+numbers[i][1]);
      }
    }
    if(numberList.Count() > 0)
    {
    if(numberList[0] == 1)
    {
      answer++;
      numberList.RemoveAt(0);
      numberPartnerList.RemoveAt(0);
      numberCountList.RemoveAt(0);
    }
    }

    //Console.WriteLine(answer);
    
    if(numberList.Count() > 0)
    {
    long[,] compressNums = new long[2*numberList.Count(),2];

    for(int i = 0; i < numberList.Count(); i++)
    {
      compressNums[i,0] = numberList[i];
      compressNums[i,1] = i;
      compressNums[numberList.Count()+i,0] = numberPartnerList[i];
      compressNums[numberList.Count()+i,1] = numberList.Count()+i;
    }
    compressNums = CoordinateCompression(compressNums);
    
    
    long[] answers = new long[2*numberList.Count()];
    for(int i = 0; i < numberList.Count(); i++)
    {
      answers[compressNums[i,2]] = numberCountList[i];
    }
    for(int i = 0; i < numberList.Count(); i++)
    {
      if(answers[compressNums[numberList.Count()+i,2]] != 0)
      {
        answers[compressNums[numberList.Count()+i,2]]
          = Math.Max(answers[compressNums[numberList.Count()+i,2]],numberCountList[i]);
        answers[compressNums[i,2]] = 0;
      }
    }
    
    for(int i = 0; i < 2*numberList.Count(); i++) answer += answers[i];
    }
    
		Console.WriteLine(answer);
    
	}

  static long[,] CoordinateCompression(long[,] vertexesOriginal)
    //1次元の座標圧縮。配列は0-indexで引数[x,順番]から[x,順番,圧縮x]を返す。
    //例えば、{2,4,4,3}は{0,2,2,1}を返す。
  {
    long n = vertexesOriginal.GetLength(0);//多次元配列の長さの取得
    long xCount = 1;//圧縮後のxの個数
    long[] compressedXmemo = new long[n];
    long[][] vertexes = new long[n][];
    for(long i = 0; i < n; i++)
    {
      vertexes[i] = new long[3];
      for(long j = 0; j < 2; j++)
      {
        vertexes[i][j] = vertexesOriginal[i,j];//配列をジャグ配列に置換
      }
    }

    Array.Sort(vertexes, (a, b) => a[0].CompareTo(b[0]));//x座標でソート
    vertexes[0][2] = 0;
    compressedXmemo[0] = vertexes[0][0];
    for(long i = 1; i < n; i++)
    {
      if(vertexes[i][0] == vertexes[i-1][0])//x座標が同じ
      {
        vertexes[i][2] = xCount - 1;
      }else
      {
        xCount++;
        vertexes[i][2] = xCount - 1;
        compressedXmemo[xCount-1] = vertexes[i][0];
      }
    }
    long[] compressedX = new long[xCount];
    for(long i = 0; i < xCount; i++) compressedX[i] = compressedXmemo[i];

    Array.Sort(vertexes, (a, b) => a[1].CompareTo(b[1]));//もとの番号順でソート

    long[,] answer = new long[n,3];
    for(long i = 0; i < n; i++)
    {
      for(long j = 0; j < 3; j++)
      {
        answer[i,j] = vertexes[i][j];//ジャグ配列から戻す
      }
    }

    return answer;//[x,順番,圧縮x]を返す。
  }

  static List<long> GetPrimeList(long maxPrime)//「引数1以上」max以下の素数のリストを返す。エラトステネスの篩を用いる。
  {
    List<long> answers = new List<long>();
    bool[] isPrimes = new bool[maxPrime];//素数リスト
    isPrimes[0] = false;//1は素数でないのでfalse
    for(long i = 1; i < maxPrime; i++) isPrimes[i] = true;//素数と仮定してtrue
  
    for(long i = 2; i <= Math.Sqrt(maxPrime); i++)
    {
      if(isPrimes[i-1])
      {
        for (long j = i*2; j <= maxPrime; j+=i)
        {
          isPrimes[j-1] = false;//素数でないのでfalse
        }
      }
    }

    for(int i = 0; i < maxPrime; i++)
    {
      if(isPrimes[i]) answers.Add(i+1);
    }

    return answers;
  }
  
}