Problem:
Score : 300 points

Problem Statement

There are N people, conveniently numbered 1 through N.
They were standing in a row yesterday, but now they are unsure of the order in which they were standing.
However, each person remembered the following fact: the absolute difference of the number of the people who were standing to the left of that person, and the number of the people who were standing to the right of that person.
According to their reports, the difference above for person i is A_i.

Based on these reports, find the number of the possible orders in which they were standing.
Since it can be extremely large, print the answer modulo 10^9+7.
Note that the reports may be incorrect and thus there may be no consistent order.
In such a case, print 0.

Constraints

1≦N≦10^5

0≦A_i≦N-1

Input

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

N
A_1 A_2 ... A_N

Output

Print the number of the possible orders in which they were standing, modulo 10^9+7.

Sample Input 1

5
2 4 4 0 2

Sample Output 1

4

There are four possible orders, as follows:

2,1,4,5,3

2,5,4,1,3

3,1,4,5,2

3,5,4,1,2

Sample Input 2

7
6 4 0 2 4 0 2

Sample Output 2

0

Any order would be inconsistent with the reports, thus the answer is 0.

Sample Input 3

8
7 5 1 1 7 3 5 3

Sample Output 3

16

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

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace ARC066C
{
    class Program
    {
        static void Solve(Input input)
        {
            var n = input.NextInt();
            var a = input.NextInt(n);
            var p = new int[n];

            // 両端はn-1個
            // そこから一つ内側に行くにつれて2ずつ値が減っていく
            for (int i = 0; i < n; i++)
            {
                if (a[i] == 0)
                {
                    p[n / 2]++;
                }
                else
                {
                    p[(n - a[i]) / 2]++;
                    p[n - (n - a[i]) / 2 - 1]++;
                }
            }


            var mod = new Modulo();
            long pat = mod.Power(2, n / 2);

            // 奇数個の場合、中央は必ず0
            if (n % 2 != 0)
            {
                if (p[n / 2] != 1)
                {
                    pat = 0;
                }
            }
            // 中央以外は必ずペア
            if (!p.Take(n / 2).Concat(p.Skip(n / 2 + 1)).All(x => x == 2))
            {
                pat = 0;
            }

            Console.WriteLine(pat);
        }

        /// <summary>
        /// Mを法とする数値
        /// </summary>
        class Modulo
        {
            public int M { get; private set; }
            private bool mIsPrime;
            int[] factorials;
            int[] invs;
            int[] finvs;

            public Modulo(int m = (int)1e9 + 7)
            {
                this.M = m;
                this.mIsPrime = isPrime(m);

                factorials = Enumerable.Repeat<int>(1, 2).ToArray();
                invs = Enumerable.Repeat<int>(1, 2).ToArray();
                finvs = Enumerable.Repeat<int>(1, 2).ToArray();
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public int Add(int a, int b)
            {
                return (a + b) % this.M;
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public int Sub(int a, int b)
            {
                return (a + this.M - b) % this.M;
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public int Mul(int a, int b)
            {
                return (int)(((long)(a % this.M) * (b % this.M)) % this.M);
            }

#if false
            /// <summary>
            /// x^y
            /// 繰り返し二乗法
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <returns></returns>
            public long Power(long x, long y)
            {
                switch (y)
                {
                    case 0: return 1;
                    case 1: return x % this.M;

                    default:
                        long z1 = Power(x, y / 2);
                        long z2 = Mul(z1, z1);

                        if ((y % 2) == 0)
                        {
                            return z2;
                        }
                        else
                        {
                            return Mul(z2, x);
                        }
                }
            }
#endif

            /// <summary>
            /// x^y
            /// 繰り返し二乗法（バイナリ）
            /// </summary>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <returns></returns>
            public int Power(int x, int y)
            {
                var r = 1;

                while (y > 0)
                {
                    if ((y & 1) != 0)
                    {
                        r = Mul(r, x);
                    }
                    x = Mul(x, x);

                    y >>= 1;
                }

                return r;
            }

            /// <summary>
            /// aの逆元（a^-1）
            /// Mとaが互いに素である必要があるが
            /// 大抵Mが素数でa<Mなのでチェックしない
            /// </summary>
            /// <param name="a"></param>
            /// <returns></returns>
            public int Inverse(int a)
            {
                int b = this.M;
                int u = 1, v = 0;

                while (b > 0)
                {
                    int t = a / b;
                    a -= t * b;
                    swap(ref a, ref b);
                    u -= t * v;
                    swap(ref u, ref v);
                }

                u %= M;
                if (u < 0) u += this.M;

                return u;
            }

            /// <summary>
            /// a/b
            /// </summary>
            /// <param name="a"></param>
            /// <param name="b"></param>
            /// <returns></returns>
            public int Div(int a, int b)
            {
                if (!this.mIsPrime)
                {
                    throw new ArgumentException();
                }

                return Mul(a, Inverse(b));
            }

            /// <summary>
            /// a以下の階乗のテーブルを作成する
            /// </summary>
            /// <param name="a"></param>
            void factTable(int a)
            {
                if (a >= factorials.Length)
                {
                    int begin = factorials.Length;

                    Array.Resize(ref factorials, a + 1);

                    for (int i = begin; i <= a; i++)
                    {
                        factorials[i] = Mul(factorials[i - 1], i);
                    }
                }
            }

            /// <summary>
            /// a以下の逆元テーブルを作成する
            /// </summary>
            /// <param name="a"></param>
            void invTable(int a)
            {
                if (a >= invs.Length)
                {
                    int begin = invs.Length;

                    Array.Resize(ref invs, a + 1);

                    for (int i = begin; i <= a; i++)
                    {
                        invs[i] = this.M - Mul(invs[this.M % i], this.M / i);
                    }
                }
            }

            /// <summary>
            /// a以下の階乗の逆元テーブルを作成する
            /// </summary>
            /// <param name="a"></param>
            void finvTable(int a)
            {
                if (a >= finvs.Length)
                {
                    invTable(a);

                    int begin = finvs.Length;

                    Array.Resize(ref finvs, a + 1);

                    for (int i = begin; i <= a; i++)
                    {
                        finvs[i] = Mul(finvs[i - 1], invs[i]);
                    }
                }
            }

            /// <summary>
            /// nCr
            /// </summary>
            /// <param name="n"></param>
            /// <param name="r"></param>
            /// <returns></returns>
            public int Ncr(int n, int r)
            {
                if (n == r) return 1;

                if (factorials.Length <= n) factTable(n);
                if (finvs.Length <= n) finvTable(n);

                return Mul(factorials[n], Mul(finvs[r], finvs[n - r]));
            }

            /// <summary>
            /// 素数か
            /// </summary>
            /// <param name="x"></param>
            /// <returns></returns>
            static bool isPrime(long x)
            {
                if (x < 2)
                {
                    return false;
                }
                if (x == 2)
                {
                    return true;
                }
                if ((x % 2) == 0)
                {
                    return false;
                }

                long rx = (long)(Math.Sqrt(x) + 1);
                for (long n = 3; n < rx; n += 2)
                {
                    if ((x % n) == 0)
                    {
                        return false;
                    }
                }

                return true;
            }

            static void swap<T>(ref T a, ref T b)
            {
                T temp = a;
                a = b;
                b = temp;
            }
        }

        #region Main
        public static void Main(string[] args)
        {
            // 出力が少ないときはこれをセットする方が時間かかるけれど
            // その場合は出力がボトルネックになっていないので、まあ良しとする
            var needsFlushOutput = true;
            if (needsFlushOutput)
            {
                // 細かく出力しないようにする
                var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
                Console.SetOut(sw);
            }

            // 通常は引数無しで、ファイルから読み出したい場合はpath指定する
            var input = new Input();
            // 仮想的に標準入力をセットする
            //input.SetText("");

            Solve(input);

            Console.Out.Flush();
        }
        #endregion

        #region Competitive Template
#pragma warning disable CS0414
        static readonly int MOD = (int)1e9 + 7;
#pragma warning restore CS0414

        class Input
        {
            // 変な改行コードがたまに混じっているので、一応セパレート指定する
            // スペース単独指定の方がもちろん早い
            static readonly char[] separator = { ' ', '\r', '\n' };
            Queue<string> q { get; set; }
            StreamReader sr;

            /// <summary>
            /// 特定のファイルから読み出したい場合はpath指定する
            /// </summary>
            /// <param name="path"></param>
            public Input(string path = "")
            {
                q = new Queue<string>();

                if (string.IsNullOrEmpty(path))
                {
                    sr = new StreamReader(Console.OpenStandardInput());
                }
                else
                {
                    sr = new StreamReader(path);
                }
            }

            /// <summary>
            /// 入力予約
            /// </summary>
            /// <param name="items"></param>
            public void SetText(IEnumerable<string> items)
            {
                foreach (var item in items)
                {
                    SetText(item);
                }
            }

            /// <summary>
            /// 入力予約
            /// </summary>
            /// <param name="s"></param>
            /// <returns></returns>
            public bool SetText(string s)
            {
                if (s == null) return false;

                foreach (var elem in s.Trim().Split(separator, StringSplitOptions.RemoveEmptyEntries))
                {
                    q.Enqueue(elem);
                }

                return true;
            }

            /// <summary>
            /// 内部queueに入力からの値をsplitして格納する
            /// </summary>
            /// <returns></returns>
            bool read()
            {
                var s = sr.ReadLine();
                if (s == null) return false;

                foreach (var elem in s.Trim().Split(separator, StringSplitOptions.RemoveEmptyEntries))
                {
                    q.Enqueue(elem);
                }

                if (!q.Any()) return read();

                return true;
            }

            /// <summary>
            /// 次のstringを一つ読み込む
            /// </summary>
            /// <returns></returns>
            public string Next()
            {
                if (!q.Any())
                {
                    if (!read())
                    {
                        return "";
                    }
                }

                return q.Dequeue();
            }

            /// <summary>
            /// 指定個数queueにたまるまでenqueueし続ける
            /// </summary>
            /// <param name="n"></param>
            /// <returns></returns>
            bool accumulate(int n)
            {
                while (q.Count() < n)
                {
                    if (!read()) return false;
                }

                return true;
            }

            public int NextInt() => int.Parse(Next());
            public long NextLong() => long.Parse(Next());
            public double NextDouble() => double.Parse(Next());

            /// <summary>
            /// n個の要素をparseして、それぞれにoffsetをaddした配列を返す
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="n"></param>
            /// <param name="offset"></param>
            /// <param name="parse"></param>
            /// <param name="add"></param>
            /// <returns></returns>
            T[] NextT<T>(int n, T offset, Func<string, T> parse, Func<T, T, T> add)
            {
                if (!accumulate(n)) return null;

                var a = new T[n];

                for (int i = 0; i < n; i++)
                    a[i] = add(parse(q.Dequeue()), offset);

                return a;
            }

            public string[] Next(int n) => NextT(n, "", x => x, (x, y) => x);
            public int[] NextInt(int n, int offset = 0) => NextT(n, offset, int.Parse, (x, y) => x + y);
            public long[] NextLong(int n, long offset = 0) => NextT(n, offset, long.Parse, (x, y) => x + y);
            public double[] NextDouble(int n, double offset = 0.0) => NextT(n, offset, double.Parse, (x, y) => x + y);
        }

        static class Utils
        {
            public static void Print<T>(IEnumerable<T> ie, string sep = " ") => Console.WriteLine(string.Join(sep, ie));
            public static void Print<T>(params T[] objs) => Print(objs);
            public static T Max<T>(params T[] objs) => objs.Max();
            public static T Min<T>(params T[] objs) => objs.Min();

            public static void Swap<T>(ref T x, ref T y) where T : struct { var t = x; x = y; y = t; }
            public static void Swap<T>(IList<T> list, int i, int j) where T : struct { var t = list[i]; list[i] = list[j]; list[j] = t; }

            /// <summary>
            /// vでfillされたT[d1][d2]配列を作成する
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="d1"></param>
            /// <param name="d2"></param>
            /// <param name="v"></param>
            /// <returns></returns>
            public static T[][] Create2DArray<T>(int d1, int d2, T v = default(T))
            {
                return
                    Enumerable.Repeat(0, d1).Select(_ =>
                    Enumerable.Repeat(v, d2).ToArray()).ToArray();
            }

            /// <summary>
            /// vでfillされたT[d1][d2][d3]配列を作成する
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="d1"></param>
            /// <param name="d2"></param>
            /// <param name="d3"></param>
            /// <param name="v"></param>
            /// <returns></returns>
            public static T[][][] Create3DArray<T>(int d1, int d2, int d3, T v = default(T))
            {
                return
                    Enumerable.Repeat(0, d1).Select(_ =>
                    Enumerable.Repeat(0, d2).Select(__ =>
                     Enumerable.Repeat(v, d3).ToArray()).ToArray()).ToArray();
            }
        }
        #endregion
    }
}