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.Collections.Generic;
using System.Text;
using System.Linq;


class Program
{
    public void Proc()
    {
        Reader.IsDebug = false;
        int inputCount = int.Parse(Reader.ReadLine());

        for (int i = 0; i < inputCount; i++)
        {
            long num = long.Parse(Reader.ReadLine());
            NumList.Add(num);
        }
        NumList.Sort();

        int ans = this.GetAns(string.Empty, new Dictionary<long, bool>(), 0, 0);
        Console.WriteLine(ans);

    }


    private int GetAns(string keyString, Dictionary<long, bool> key, int idx, long lastKey) {
        if (idx >= this.NumList.Count)
        {
            return 0;
        }
        if(!dic.ContainsKey(keyString)) {
            dic.Add(keyString, new Dictionary<int,int>());
        }
        if(dic[keyString].ContainsKey(idx)) {
            return dic[keyString][idx];
        }

        long num = NumList[idx];
        int ans = 0;
        bool canAdd = true;
        foreach (long k in key.Keys)
        {
            if (!GetCanBoth(k, num))
            {
                canAdd = false;
                break;
            }

        }
        if (canAdd)
        {
            string newKey = keyString;
            long newLastKey = lastKey;
            bool added = false;
            if(lastKey != num) {
                newKey += "#" + num;
                newLastKey = num;
                key.Add(num, true);
                added = true;
            }
            ans = this.GetAns(newKey, key, idx + 1, newLastKey) + 1;
            if (added)
            {
                key.Remove(num);
            }
        }
        ans = Math.Max(ans, this.GetAns(keyString, key, idx + 1, lastKey));
        this.dic[keyString][idx] = ans;
        return ans;

    }

    private Dictionary<string, Dictionary<int, int>> dic = new Dictionary<string, Dictionary<int, int>>();
    private Dictionary<long, Dictionary<long, bool>> CanBoth = new Dictionary<long, Dictionary<long, bool>>();
    private List<long> NumList = new List<long>();
    private bool GetCanBoth(long num1, long num2)
    {
        if (CanBoth.ContainsKey(num1) && CanBoth[num1].ContainsKey(num2))
        {
            return CanBoth[num1][num2];
        }
        if (CanBoth.ContainsKey(num2) && CanBoth[num2].ContainsKey(num1))
        {
            return CanBoth[num2][num1];
        }
        long mul = num1 * num2;
        long ret = RippouRoot(mul, mul, 1);
        if (ret < 0)
        {
            if (!CanBoth.ContainsKey(num1))
            {
                CanBoth.Add(num1, new Dictionary<long, bool>());
            }
            CanBoth[num1][num2] = true;
            return true;
        }
        else
        {
            if (!CanBoth.ContainsKey(num1))
            {
                CanBoth.Add(num1, new Dictionary<long, bool>());
            }
            CanBoth[num1][num2] = false;
            return false;

        }
    }


    private long RippouRoot(long num, long max, long min)
    {
        long temp = (max + min) / 2;
        if(temp*temp*temp == num) {
            return temp;
        }
        if (max * max * max == num)
        {
            return max;
        }
        if (min * min * min == num)
        {
            return min;
        }
        if (temp * temp * temp > num)
        {
            if (max - min <= 1)
            {
                return -1;
            }
            else
            {
                return RippouRoot(num, temp, min);
            }
        }
        else
        {
            if (max - min <= 1)
            {
                return -1;
            }
            else
            {
                return RippouRoot(num, max, temp);
            }

        }
        
    }


    public class Reader
    {
        public static bool IsDebug = true;
        private static String PlainInput = @"


6
2
4
8
16
32
64

";
        private static System.IO.StringReader Sr = null;
        public static string ReadLine()
        {
            if (IsDebug)
            {
                if (Sr == null)
                {
                    Sr = new System.IO.StringReader(PlainInput.Trim());
                }
                return Sr.ReadLine();
            }
            else
            {
                return Console.ReadLine();
            }
        }
    }
    static void Main()
    {
        Program prg = new Program();
        prg.Proc();
    }
}