Problem:
Score : 500 points

Problem Statement

An adult game master and N children are playing a game on an ice rink.
The game consists of K rounds.
In the i-th round, the game master announces:

Form groups consisting of A_i children each!

Then the children who are still in the game form as many groups of A_i children as possible.
One child may belong to at most one group.
Those who are left without a group leave the game. The others proceed to the next round.
Note that it's possible that nobody leaves the game in some round.

In the end, after the K-th round, there are exactly two children left, and they are declared the winners.

You have heard the values of A_1, A_2, ..., A_K. You don't know N, but you want to estimate it.

Find the smallest and the largest possible number of children in the game before the start, or determine that no valid values of N exist.

Constraints

1 \leq K \leq 10^5

2 \leq A_i \leq 10^9

All input values are integers.

Input

Input is given from Standard Input in the following format:

K
A_1 A_2 ... A_K

Output

Print two integers representing the smallest and the largest possible value of N, respectively,
or a single integer -1 if the described situation is impossible.

Sample Input 1

4
3 4 3 2

Sample Output 1

6 8

For example, if the game starts with 6 children, then it proceeds as follows:

In the first round, 6 children form 2 groups of 3 children, and nobody leaves the game.

In the second round, 6 children form 1 group of 4 children, and 2 children leave the game.

In the third round, 4 children form 1 group of 3 children, and 1 child leaves the game.

In the fourth round, 3 children form 1 group of 2 children, and 1 child leaves the game.

The last 2 children are declared the winners.

Sample Input 2

5
3 4 100 3 2

Sample Output 2

-1

This situation is impossible.
In particular, if the game starts with less than 100 children, everyone leaves after the third round.

Sample Input 3

10
2 2 2 2 2 2 2 2 2 2

Sample Output 3

2 3

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;   
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

class Program{

    const long MOD = (long)1e9 + 7;

    static void Main(string[] args)
    {
        // main ↓
        bool dekiru = true;
       long max = 2;
       long min = 2;
        int n = ScanOneInt();
        long[] k = ScanLongArray(n);
        for (int i = n - 1; i >= 0; i--)
        {
            if (min <= (min + k[i] - 1) / k[i] * k[i ]&& (min + k[i] - 1) / k[i] * k[i] <= max && 
                min <= max / k[i] * k[i] && max / k[i] * k[i] <= max)
            {
                min = (min + k[i] - 1) / k[i] * k[i];
                max = max / k[i] * k[i] + k[i] - 1;
            }
            else
            {
                dekiru = false;
            }
        }


        Console.WriteLine(dekiru ? (min + " " + max) : "-1");


        // main↑
    }

    public class SegmentTree<Monoid>
    {
        private bool debug = false;
        // 0-indexed SegmentTree
        public int size;
        Monoid[] segTree;
        Monoid unit;
        public SegmentTree(int inSize, Monoid unit)
        {
            this.unit = unit;
            size = 2;
            while (size < inSize) size *= 2;
            segTree = new Monoid[2 * size];
            for (int i = 0; i < segTree.Length; i++)
            {
                segTree[i] = unit;
                // 単位元なので全部に代入しておけばよい
            }
        }

        public int BinarySearch(int x)
        {
            int high = size;
            int low = 0;
            int mid = (high + low) / 2;
            while (low < high)
            {
                // Console.WriteLine(low + " " + high);
                Monoid cmp = NonRecursiveQuery(0, mid);
                // Console.WriteLine("[ 0, " + mid + " ] -> " + cmp);
                if ((dynamic)cmp >= x)
                {
                    high = mid;
                }
                else if ((dynamic)cmp < x)
                {
                    low = mid + 1;
                }
                mid = (high + low) / 2;
            }
            return mid;
        }

        public void set(int iter, Monoid val)
        {
            segTree[iter + size - 1] = val;
        }

        public Monoid GetMonoid(int index)
        {
            return segTree[index + size - 1];
        }

        public void add(int iter, Monoid val)
        {
            update(iter, (dynamic)segTree[iter + size - 1] + (dynamic)val);
        }

        public void build()
        {
            // 2段目より上の構築
            for (int k = size - 2; k >= 0; k--)
            {
                segTree[k] = F(segTree[2 * k + 1], segTree[2 * k + 2]);
            }
        }

        public Monoid F(Monoid m1, Monoid m2)
        {
            Monoid res = GCD(m1, m2);
            return res;
        }

        public Monoid Query(int ql, int qr, int index, int nl, int nr)
        {
            // Recursive
            // [ql, qr)について考える
            // index に対応する範囲 nl <= x <= nr
            if(nr <= ql || qr <= nl)
            {
                if (debug) Console.WriteLine("Out: [l , r), [" + nl + " ," + nr + "), " + "index: " + index + " ,unit:" + unit);
                return unit;
            }
            if(ql <= nl && nr <= qr)
            {
                if (debug) Console.WriteLine("All in [l , r): [" + nl + " ," + nr + "), " + "index: " + index + ",rangeAns:" + segTree[index]);
                return segTree[index];
            }
            else
            {
                Monoid vl = Query(ql, qr, index * 2 + 1, nl, (nl + nr) / 2);
                Monoid vr = Query(ql, qr, index * 2 + 2, (nl + nr) / 2, nr);
                if (debug) Console.WriteLine("Recursive: [l , r),+: [" + nl + ", " + nr + "), index->"+ index);
                if (debug) Console.WriteLine("(vl, vr): (" + vl + ", " + vr + ")");
                return F(vl, vr);
            }
        }

        public Monoid NonRecursiveQuery(int l,int r)
        {
            // 区間　-> [ l , r ]
            // 非再帰
            int L = l + size - 1;
            int R = r + size - 1;
            Monoid s = unit;
            while (L <= R)
            {
                if(L % 2 == 0)
                {
                    s = F(s, segTree[L]);
                    L++;
                }
                if(R % 2 == 1)
                {
                    s = F(s, segTree[R]);
                }
                L /= 2;
                R /= 2;
                R--;
            }
            return s;
        }

        public void update(int k, Monoid x)
        {
            k += size-1;
            segTree[k] = x;
            while (true)
            {
                k = (k - 1) / 2;
                segTree[k] = F(segTree[k * 2 + 1], segTree[k * 2 + 2]);
                if(k == 0)
                {
                    break;
                }
            }
        }

        public void print()
        {
            Console.Write("Print:");
            for (int i = 0; i < segTree.Length; i++)
            {
                Console.Write(segTree[i] + " ");
            }
            Console.WriteLine("");
        }

    }

    static int Nibutan(long[] array, long k)
    {
        int low = 0 ;
        int high = array.Length-1;
        int mid = high;

        while (low < high)
        {
            mid = (low + high) / 2;
            if(array[mid] >= k) // k以上の最小index.ココを変えるだけ
            {
                high = mid;
            }
            else
            {
                low = mid + 1;
            }
            mid = (low + high) / 2;
        }
        return mid;
    }   

    static int ScanOneInt()
    {
        return int.Parse(Console.ReadLine());
    }

    static long ScanOneLong()
    {
        return long.Parse(Console.ReadLine());
    }

    static string[] ScanStringArray(int lines)
    {
        string[] str = new string[lines];
        for (int i = 0; i < lines; i++)
        {
            str[i] = Console.ReadLine();
        }
        return str;
    }

    static int[] ScanIntArray(int t)
    {
        string[] str = Console.ReadLine().Split(' ');
        int[] ar = new int[t];
        for (int i = 0; i < t; i++)
        {
            ar[i] = int.Parse(str[i]);
        }
        return ar;
    }

    static string[] ScanStringArrayInOneLine(int t)
    {
        string[] str = Console.ReadLine().Split(' ');
        return str;
    }

    static Tuple<int,int> ScanTuple()
    {
        int[] tmp = ScanIntArray(2);
        return new Tuple<int, int>(tmp[0], tmp[1] * -1);
    }

    static long[] ScanLongArray(int t)
    {
        string[] str = Console.ReadLine().Split(' ');
        long[] ar = new long[t];
        for (int i = 0; i < t; i++)
        {
            ar[i] = long.Parse(str[i]);
        }
        return ar;
    }

    static double[] ScanDoubleArray(int t)
    {
        string[] str = Console.ReadLine().Split(' ');
        double[] ar = new double[t];
        for (int i = 0; i < t; i++)
        {
            ar[i] = double.Parse(str[i]);
        }
        return ar;
    }

    static int[,] ScanIntSquareArray(int h, int w)
    {
        int[,] resAr = new int[h, w];
        for (int i = 0; i < h; i++)
        {
            int[] ar = ScanIntArray(w);
            for (int j = 0; j < w; j++)
            {
                resAr[i, j] = ar[j];
            }
        }
        return resAr;
    }

    static int[,] CombModMake()
    {
        int[,] res = new int[2005, 2005];
        for (int i = 0; i < 2005; i++)
        {
            for (int j = 0; j < 2005; j++)
            {
                if (i == 0 || i < j)
                {
                    res[i, j] = 0;
                }
                else if (j == 0 || i == j)
                {
                    res[i, j] = 1;
                }
                else
                {
                    res[i, j] = (res[i - 1, j] + res[i - 1, j - 1]) % (int)MOD;
                }
            }
        }
        return res;
    }

    // int, long両方で出来るGCDを作った
    static T GCD<T>(T a, T b)
    {
        if((dynamic)b == 0)
        {
            return a;
        }
        else
        {
            return GCD((dynamic)b, (dynamic)a % (dynamic)b);
        }
    }

    static bool isPrime(int a) //O(sqrt N)
    {
        int max = (int)Math.Sqrt(a);
        if (a == 1) return false;
        for (int i = 2; i <= max; i++)
        {
            if(a % i == 0)
            {
                return false;
            }
        }
        return true;
    }
}

public class Pair<T,U>: IComparable where T : IComparable<T> where U: IComparable<U>
{
    public T Item1;
    public U Item2;

    public int  CompareTo(object obj)
    {
        Pair<T,U> pair = (Pair<T, U>)obj;
        return Item1.CompareTo(pair.Item1);
    }
    public Pair(T t, U u)
    {
        Item1 = t;
        Item2 = u;
    }

}

// 計算量: Push -> O(logN), Peek -> O(1)。Clear() -> O(logN)
// ToArray()で配列化
public class PriorityQueue<T> where T : IComparable
{
    private IComparer<T> _comparer = null;
    private int _type = 0;
    private T[] _heap;
    private int _sz = 0;
    private int _count = 0;

    /// <summary>
    /// Priority Queue with custom comparer
    /// </summary>
    public PriorityQueue(int maxSize, IComparer<T> comparer)
    {
        _heap = new T[maxSize];
        _comparer = comparer;
    }

    /*
     -> Item: _heap(ヒープを持つ.sizeはmaxSize)
     -> Item: _type -> 0:asc(昇順), 1:desc(降順)
     */
    public PriorityQueue(int maxSize, int type = 0)
    {
        _heap = new T[maxSize];
        _type = type;
    }

    private int Compare(T x, T y)
    {
        if (_comparer != null) return _comparer.Compare(x, y);
        return _type == 0 ? x.CompareTo(y) : y.CompareTo(x);
        // typeが0なら昇順, 1なら降順にソートするCompare
    }

    public void Push(T x)
    {
        _count++;

        //node number
        var i = _sz++;

        while (i > 0)
        {
            //parent node number
            var p = (i - 1) / 2;
            if (Compare(_heap[p], x) <= 0) break;
            _heap[i] = _heap[p];
            i = p;
        }

        _heap[i] = x;
    }

    public T Pop()
    {
        _count--;

        T ret = _heap[0];
        T x = _heap[--_sz];

        int i = 0;
        while (i * 2 + 1 < _sz)
        {
            //children
            int a = i * 2 + 1;
            int b = i * 2 + 2;

            if (b < _sz && Compare(_heap[b], _heap[a]) < 0) a = b;

            if (Compare(_heap[a], x) >= 0) break;

            _heap[i] = _heap[a];
            i = a;
        }

        _heap[i] = x;

        return ret;
    }

    public int Count()  // O(1)
    {
        return _count;
    }

    public T Peek() // O(1)
    {
        return _heap[0];
    }

    public bool Contains(T x)   // O(N)なので注意
    {
        for (int i = 0; i < _sz; i++) if (x.Equals(_heap[i])) return true;
        return false;
    }

    public void Clear()
    {
        while (Count() > 0) Pop();
    }

    public IEnumerator<T> GetEnumerator() // なにこれ
    {
        var ret = new List<T>();

        while (Count() > 0)
        {
            ret.Add(Pop());
        }

        foreach (var r in ret)
        {
            Push(r);
            yield return r;
        }
    }

    public T[] ToArray()
    {
        T[] array = new T[_sz];
        int i = 0;

        foreach (var r in this)
        {
            array[i++] = r;
        }

        return array;
    }
}

public class GenericOperation<T>
{
    public GenericOperation()
    {
        var availableT = new Type[]
        {
            typeof(int), typeof(uint), typeof(short), typeof(ushort), typeof(long), typeof(ulong), typeof(byte),
            typeof(decimal), typeof(double)
        };
        if (!availableT.Contains(typeof(T)))
        {
            throw new NotSupportedException();
        }
        var p1 = Expression.Parameter(typeof(T));
        var p2 = Expression.Parameter(typeof(T));
        Add = Expression.Lambda<Func<T, T, T>>(Expression.Add(p1, p2), p1, p2).Compile();
        Subtract = Expression.Lambda<Func<T, T, T>>(Expression.Subtract(p1, p2), p1, p2).Compile();
        Multiply = Expression.Lambda<Func<T, T, T>>(Expression.Multiply(p1, p2), p1, p2).Compile();
        Divide = Expression.Lambda<Func<T, T, T>>(Expression.Divide(p1, p2), p1, p2).Compile();
        Modulo = Expression.Lambda<Func<T, T, T>>(Expression.Modulo(p1, p2), p1, p2).Compile();
        Equal = Expression.Lambda<Func<T, T, bool>>(Expression.Equal(p1, p2), p1, p2).Compile();
        GreaterThan = Expression.Lambda<Func<T, T, bool>>(Expression.GreaterThan(p1, p2), p1, p2).Compile();
        GreaterThanOrEqual = Expression.Lambda<Func<T, T, bool>>(Expression.GreaterThanOrEqual(p1, p2), p1, p2).Compile();
        LessThan = Expression.Lambda<Func<T, T, bool>>(Expression.LessThan(p1, p2), p1, p2).Compile();
        LessThanOrEqual = Expression.Lambda<Func<T, T, bool>>(Expression.LessThanOrEqual(p1, p2), p1, p2).Compile();
    }

    public Func<T, T, T> Add { get; private set; }
    public Func<T, T, T> Subtract { get; private set; }
    public Func<T, T, T> Multiply { get; private set; }
    public Func<T, T, T> Divide { get; private set; }
    public Func<T, T, T> Modulo { get; private set; }
    public Func<T, T, bool> Equal { get; private set; }
    public Func<T, T, bool> GreaterThan { get; private set; }
    public Func<T, T, bool> GreaterThanOrEqual { get; private set; }
    public Func<T, T, bool> LessThan { get; private set; }
    public Func<T, T, bool> LessThanOrEqual { get; private set; }
}