import java.util.*; import java.io.*; import java.lang.*; class Solution { static FastReader fr = new FastReader(); static BufferedReader read = new BufferedReader(new InputStreamReader(System.in)); static Scanner sc = new Scanner(System.in); static PrintWriter out2 = new PrintWriter(System.out); static OutputWriter out = new OutputWriter(System.out); static int MOD = 1000000007, prime[]; static long gcd, x, y; static boolean isPrime[]; public static void main(String[] args) throws IOException { int n = fr.nextInt(); long arr[] = new long[n], longest_seq = 0; for(int i = 0; i < n; i++) arr[i] = fr.nextLong(); Arrays.sort(arr); HashMap hm = new HashMap(); hm.put(1L, 1L); //System.out.println(hm); for(int i = 0; i < n; i++) { long len_stick = arr[i]; longest_seq += GetLongestSeqOfThisStick(len_stick, hm); //System.out.println(longest_seq); } out.printLine(longest_seq); out.flush(); out.close(); } public static long GetLongestSeqOfThisStick(long len_stick, HashMap hm) { if(hm.containsKey(len_stick)) return hm.get(len_stick); return GetLongestSeqOfThisStickUtil(len_stick, hm); } public static long GetLongestSeqOfThisStickUtil(long len_stick, HashMap hm) { long max = 0, n = len_stick, len_each_part = 0, multiplier = 0; while(n % 2 == 0) n /= 2; if(n == 1) { len_each_part = len_stick / 2; multiplier = 2; } for(int i = 3; i <= Math.sqrt(n); i += 2) { while(n % i == 0) n /= i; if(n == 1) { len_each_part = len_stick / i; multiplier = i; } } if(n > 2) { len_each_part = len_stick / n; multiplier = n; } if(hm.containsKey(len_each_part)) max = (multiplier * hm.get(len_each_part)) + 1; else { max = (multiplier * GetLongestSeqOfThisStick(len_each_part, hm)) + 1; hm.put(len_stick, max); } return max; } static long min_3(long a, long b, long c) { if(a < b && a < c) return a; if(b < c) return b; return c; } static long max_3(long x, long y, long z) { if(x >= y && x >= z) return x; if(y >= x && y >= z) return y; return z; } static long power(long x, long y, long m) { if (y == 0) return 1; long p = power(x, y/2, m) % m; p = (p * p) % m; return (y%2 == 0) ? p : (x * p) % m; } public static void prime_sieve(int N) { isPrime = new boolean[N+1]; Arrays.fill(isPrime, true); isPrime[0] = isPrime[1] = false; for(int i = 2; i*i <= N; i++) { if(isPrime[i]==true) { for(int j = 2*i; j <= N; j += i) { isPrime[j] = false; prime[j] = i; } } } } public static long factorial(long num) { long result = 1; for(int i = 1; i <= num; i++) result = result * (long)i; return result; } public static void ExtendedEuclidian(long a, long b) { if(b == 0) { gcd = a; x = 1; y = 0; } else { ExtendedEuclidian(b, a%b); int temp = (int) x; x = y; y = temp - (a/b)*y; } } public static long GCD(long n, long m) { if(m != 0) return GCD(m, n % m); else return n; } static class CustomComp implements Comparator { @Override public int compare(Object o1, Object o2) { return 1; } } } //Program for range minimum query using segment tree class SegmentTreeRMQ { int st[]; int count = 0;//array to store segment tree // A utility function to get minimum of two numbers int maxVal(int x, int y) { return (x > y) ? x : y; } // A utility function to get the middle index from corner // indexes. int getMid(int s, int e) { return s + (e - s) / 2; } /* A recursive function to get the minimum value in a given range of array indexes. The following are parameters for this function. st --> Pointer to segment tree index --> Index of current node in the segment tree. Initially 0 is passed as root is always at index 0 ss & se --> Starting and ending indexes of the segment represented by current node, i.e., st[index] qs & qe --> Starting and ending indexes of query range */ void RMQUtil(int ss, int se, int qs, int qe, int index, int a, int b) { // If segment of this node is a part of given range, then // return the min of the segment if (qs <= ss && qe >= se) { if(st[index]>=a && st[index]<=b) { count++; } if(ss == se) return; } // If segment of this node is outside the given range if (se < qs || ss > qe) return; // If a part of this segment overlaps with the given range int mid = getMid(ss, se); RMQUtil(ss, mid, qs, qe, 2 * index + 1, a, b); RMQUtil(mid + 1, se, qs, qe, 2 * index + 2, a, b); } // Return minimum of elements in range from index qs (quey // start) to qe (query end). It mainly uses RMQUtil() void RMQ(int n, int qs, int qe, int a, int b) { // Check for erroneous input values if (qs < 0 || qe > n - 1 || qs > qe) { System.out.println("Invalid Input"); return; } RMQUtil(0, n - 1, qs, qe, 0, a, b); System.out.println(count); count = 0; } // A recursive function that constructs Segment Tree for // array[ss..se]. si is index of current node in segment tree st int constructSTUtil(int arr[], int ss, int se, int si) { // If there is one element in array, store it in current // node of segment tree and return if (ss == se) { st[si] = arr[ss]; return arr[ss]; } // If there are more than one elements, then recur for left and // right subtrees and store the minimum of two values in this node int mid = getMid(ss, se); st[si] = maxVal(constructSTUtil(arr, ss, mid, si * 2 + 1), constructSTUtil(arr, mid + 1, se, si * 2 + 2)); return st[si]; } /* Function to construct segment tree from given array. This function allocates memory for segment tree and calls constructSTUtil() to fill the allocated memory */ void constructST(int arr[], int n) { // Allocate memory for segment tree //Height of segment tree int x = (int) (Math.ceil(Math.log(n) / Math.log(2))); //Maximum size of segment tree int max_size = 2 * (int) Math.pow(2, x) - 1; st = new int[max_size]; // allocate memory // Fill the allocated memory st constructSTUtil(arr, 0, n - 1, 0); for(int i = 0; i < st.length; i++) System.out.print(st[i]+" "); System.out.println(); } } //This code is contributed by Ankur Narain Verma class Graph { private int V; private LinkedList adj[]; boolean visited[]; Graph(int v) { V = v + 1; visited = new boolean[V]; adj = new LinkedList[V]; for(int i = 0; i < V; ++i) adj[i] = new LinkedList(); } void addEdge(int v, int w) { adj[v].add(w); adj[w].add(v); } // prints BFS traversal from a given source s void BFS(int s) { LinkedList queue = new LinkedList(); visited[s] = true; queue.add(s); while (queue.size() != 0) { s = queue.poll(); System.out.print(s+" "); Iterator i = adj[s].listIterator(); while (i.hasNext()) { int n = i.next(); if (!visited[n]) { visited[n] = true; queue.add(n); } } } } void DFS(int v, boolean visited[]) { visited[v] = true; System.out.print(v+" "); Iterator i = adj[v].listIterator(); while (i.hasNext()) { int n = i.next(); if (!visited[n]) DFS(n, visited); } } boolean isCyclicUndirectedUtil(int v, boolean visited[], int parent) { // Mark the current node as visited visited[v] = true; Integer i; // Recur for all the vertices adjacent to this vertex Iterator it = adj[v].iterator(); while (it.hasNext()) { i = it.next(); // If an adjacent is not visited, then recur for that // adjacent if (!visited[i]) { if (isCyclicUndirectedUtil(i, visited, v)) return true; } // If an adjacent is visited and not parent of current // vertex, then there is a cycle. else if (i != parent) return true; } return false; } // Returns true if the graph contains a cycle, else false. boolean isCyclicUndirected() { // Mark all the vertices as not visited and not part of // recursion stack boolean visited[] = new boolean[V]; // Call the recursive helper function to detect cycle in // different DFS trees for (int u = 1; u <= V; u++) if (!visited[u]) // Don't recur for u if already visited if (isCyclicUndirectedUtil(u, visited, -1)) return true; return false; } boolean isCyclicDirectedUtil(int v, boolean visited[], boolean recStack[]) { if(visited[v] == false) { // Mark the current node as visited and part of recursion stack visited[v] = true; recStack[v] = true; // Recur for all the vertices adjacent to this vertex Iterator i = adj[v].listIterator(); while(i.hasNext()) { int n = i.next(); if (!visited[n] && isCyclicDirectedUtil(n, visited, recStack)) return true; else if (recStack[n]) return true; } } recStack[v] = false; // remove the vertex from recursion stack return false; } boolean isCyclicDirected() { boolean visited[] = new boolean[V]; boolean recStack[] = new boolean[V]; for(int i = 1; i <= V; i++) if (isCyclicDirectedUtil(i, visited, recStack)) return true; return false; } void topologicalSortUtil(int v, boolean visited[], Stack stack) { // Mark the current node as visited. visited[v] = true; // Recur for all the vertices adjacent to this vertex Iterator it = adj[v].iterator(); while (it.hasNext()) { int i = it.next(); if (!visited[i]) topologicalSortUtil(i, visited, stack); } // Push current vertex to stack which stores result stack.push(new Integer(v)); } // The function to do Topological Sort. It uses recursive topologicalSortUtil() void topologicalSort() { Stack stack = new Stack(); // Mark all the vertices as not visited boolean visited[] = new boolean[V]; // Call the recursive helper function to store Topological Sort starting from all // vertices one by one for (int i = 1; i <= V; i++) if (visited[i] == false) topologicalSortUtil(i, visited, stack); // Print contents of stack while (stack.empty()==false) System.out.print(stack.pop() + " "); } } class BSTnode { int data; BSTnode left = null, right = null, parent = null; } class BinarySearchTree{ BSTnode root = null; int height = 0, max = 0, cnt = 1; HashMap hm = new HashMap<>(); void insert(int x) { BSTnode n = new BSTnode(); n.data = x; if(root == null) root = n; else { BSTnode temp = root, temp2 = null; while(temp != null) { temp2 = temp; if(x > temp.data) temp = temp.right; else temp = temp.left; } if(x > temp2.data) temp2.right = n; else temp2.left = n; n.parent = temp2; } } BSTnode search(BSTnode root, int key) { if (root == null || root.data == key) return root; if (root.data > key) return search(root.left, key); return search(root.right, key); } BSTnode deleteRec(BSTnode root, int key) { if (root == null) return root; // Otherwise, recur down the tree if (key < root.data) root.left = deleteRec(root.left, key); else if (key > root.data) root.right = deleteRec(root.right, key); // if key is same as root's key, then This is the node to be deleted else { // node with only one child or no child if (root.left == null) return root.right; else if (root.right == null) return root.left; // node with two children: Get the inorder successor (smallest in the right subtree) root.data = minValue(root.right); // Delete the inorder successor root.right = deleteRec(root.right, root.data); } return root; } int minValue(BSTnode root) { int minv = root.data; while (root.left != null) { minv = root.left.data; root = root.left; } return minv; } public int getHeight(BSTnode n) { if(n == null) return 0; height = 1 + Math.max(getHeight(n.left), getHeight(n.right)); return height; } } // KMP pattern searching class KMP_String_Matching { void KMPSearch(String pat, String txt) { int M = pat.length(); int N = txt.length(); // create lps[] that will hold the longest prefix suffix values for pattern int lps[] = new int[M]; int j = 0; // index for pat[] // Preprocess the pattern (calculate lps[] array) computeLPSArray(pat,M,lps); int i = 0; // index for txt[] while (i < N) { if (pat.charAt(j) == txt.charAt(i)) { j++; i++; } if (j == M) { System.out.println("Found pattern at index " + (i-j)); j = lps[j-1]; } // mismatch after j matches else if (i < N && pat.charAt(j) != txt.charAt(i)) { // Do not match lps[0..lps[j-1]] characters, they will match anyway if (j != 0) j = lps[j-1]; else i = i+1; } } } void computeLPSArray(String pat, int M, int lps[]) { // length of the previous longest prefix suffix int len = 0; int i = 1; lps[0] = 0; // lps[0] is always 0 // the loop calculates lps[i] for i = 1 to M-1 while (i < M) { if (pat.charAt(i) == pat.charAt(len)) { len++; lps[i] = len; i++; } else { // (pat[i] != pat[len]) // This is tricky. Consider the example. // AAACAAAA and i = 7. The idea is similar to search step. if (len != 0) { len = lps[len-1]; // Also, note that we do not increment i here } else { // if (len == 0) lps[i] = len; i++; } } } } } class FastReader { BufferedReader br; StringTokenizer st; public FastReader() { br = new BufferedReader(new InputStreamReader(System.in)); } String next() { while (st == null || !st.hasMoreElements()) { try { st = new StringTokenizer(br.readLine()); } catch (IOException e) { e.printStackTrace(); } } return st.nextToken(); } int nextInt() { return Integer.parseInt(next()); } long nextLong() { return Long.parseLong(next()); } double nextDouble() { return Double.parseDouble(next()); } String nextLine() { String str = ""; try { str = br.readLine(); } catch (IOException e) { e.printStackTrace(); } return str; } } class OutputWriter { private final PrintWriter writer; public OutputWriter(OutputStream outputStream) { writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream))); } public OutputWriter(Writer writer) { this.writer = new PrintWriter(writer); } public void print(Object...objects) { for (int i = 0; i < objects.length; i++) { if (i != 0) writer.print(' '); writer.print(objects[i]); } } public void printLine(Object...objects) { print(objects); writer.println(); } public void close() { writer.close(); } public void flush() { writer.flush(); } }