Problem:
MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

Problem J:
SolveMe

$N$ 個の部屋があり, それぞれの部屋の床にはうさぎの絵が描かれている. あなたが部屋 $r$ のうさぎの絵の右耳の部分に乗ると, あなたは部屋 $A[r]$ に書かれたうさぎのしっぽの上にテレポートする. 同様に, 部屋 $r$ のうさぎの絵の左耳の部分に乗ると, 部屋 $B[r]$ に書かれたうさぎのしっぽの上にテレポートする.

整数 $X$, $Y$, $Z$ が与えられる. ねこは, 以下の条件を満たすようにテレポートを設定しようとしている. テレポートの $N^{2N}$ 通りの設定方法のうち, 条件を満たすものは何通りか, mod 1,000,000,007で求めよ.

条件: 任意の部屋 $r$ に対し, $r$ から右耳にちょうど $X$ 回乗り, 左耳にちょうど 1 回乗り, 右耳にちょうど $Y$ 回乗り, 左耳にちょうど 1 回乗り, 右耳にちょうど $Z$ 回乗ると, $r$ に戻る.

Constraints

$N$ will be between 1 and 1,000, inclusive.

$X$, $Y$, $Z$ will be between 0 and 1,000,000,000,000,000,000, inclusive.

Input

入力は以下の形式で与えられる:

$N$ $X$ $Y$ $Z$

Output

テレポートの設定方法を 1,000,000,007 で割ったあまりを表す整数を 1 行に出力せよ.

Sample Input 1

3 1 0 1

Sample Output 1

18

Sample Input 2

5 8 5 8

Sample Output 2

120

Solution in C# (judge verdict: Wrong Answer)

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

class Myon
{
    public Myon() { }
    public static int Main()
    {
        new Myon().calc();
        return 0;
    }

    long[] kaijo;



    long mod = 1000000007;

    long[,] C;

    void getC(int MAX)
    {
        C = new long[MAX, MAX];
        for (int i = 0; i < MAX; i++)
        {
            C[i, 0] = 1;
            for (int j = 1; j <= i; j++)
            {
                C[i, j] = C[i - 1, j - 1] + C[i - 1, j];
                C[i, j] %= mod;
            }
        }
    }

    long rev(long a)
    {
        return powmod(a, mod - 2);
    }

    long powmod(long a, long p)
    {
        if (p == 0) return 1;
        if ((p & 1) == 1)
        {
            return (a * powmod(a, p - 1)) % mod;
        }
        long c = powmod(a, p >> 1);
        return (c * c) % mod;
    }

    void calc2()
    {
        int N = 3;
        int X = 2;
        int Y = 0;
        int Z = 1;

        int[] A = new int[N];
        int[] B = new int[N];
        for (A[0] = 0; A[0] < N; A[0]++)
        {
            for (B[0] = 0; B[0] < N; B[0]++)
            {
                for (A[1] = 0; A[1] < N; A[1]++)
                {
                    for (B[1] = 0; B[1] < N; B[1]++)
                    {
                        for (A[2] = 0; A[2] <  N; A[2]++)
                        {
                            for (B[2] = 0; B[2] < N; B[2]++)
                            {
                                bool flag = true;
                                for (int start = 0; start < 3; start++)
                                {
                                    int now = start;
                                    for (int i = 0; i < X; i++)
                                    {
                                        now = A[now];
                                    }
                                    now = B[now];
                                    for (int i = 0; i < Y; i++)
                                    {
                                        now = A[now];
                                    }
                                    now = B[now];
                                    for (int i = 0; i < Z; i++)
                                    {
                                        now = A[now];
                                    }
                                    if (start != now)
                                    {
                                        flag = false;
                                        break;
                                    }
                                }
                                if (flag)
                                {
                                    Console.WriteLine("{0} {1} {2}   {3} {4} {5}", A[0], A[1], A[2], B[0], B[1], B[2]);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    void calc()
    {
        //calc2();
        Scanner cin = new Scanner();

        long mod = 1000000007;
        kaijo = new long[2000];
        kaijo[0] = 1;
        for (int i = 1; i < 2000; i++)
        {
            kaijo[i] = kaijo[i - 1] * i;
            kaijo[i] %= mod;
        }
        getC(2000);


        int N = cin.nextInt();

        long X = cin.nextLong();
        long Y = cin.nextLong();
        long Z = cin.nextLong();


        long[] dp = new long[N + 1];
        dp[0] = 1;

        match(2000);

        if (X > 0 || Y > 0 || Z > 0)
        {
            if (Y == 0 || (X == 0 && Z == 0))
            {
                //long sum = X + Y + Z;
                Console.WriteLine(18);
            }
            else
            {
                for (int i = 0; i <= N; i++)
                {
                    //自己ループの場合
                    for (int j = 1; j <= N - i; j++)
                    {
                        //ステップの合計数
                        int sum = (int)((X % j) + (Y % j) + (Z % j)) % j;
                        int XZ = (int)((X % j) + (Z % j)) % j;

                        //左耳のステップ数として存在し得るもの
                        int count = 0;
                        for (int k = 0; k < j; k++)
                        {
                            if ((sum + k + k) % j == 0) count++;
                        }
                        long ret = count;
                        if (Y % j == 0 && XZ == 0) ret = dpmatch[j];

                        ret *= C[N - i - 1, j - 1]; //数字の選び方
                        ret %= mod;

                        ret *= kaijo[j - 1]; //数字の並び順
                        ret %= mod;
                        dp[i + j] += dp[i] * ret;
                        dp[i + j] %= mod;
                        //Console.WriteLine("single : " + i + " " + j + " " + ret);
                    }

                    //相互ループの場合
                    for (int j = 1; j * 2 <= N - i; j++)
                    {
                        //ステップの合計数
                        //int sum = (int)((X % j) + (Y % j) + (Z % j)) % j;
                        int XZ = (int)((X % j) + (Z % j)) % j;

                        //左耳のステップ数として存在し得るもの
                        int count = j;

                        long ret = count;

                        if (Y % j == 0 && XZ == 0)
                        {
                            ret = dpmatch[j + j];
                            ret -= dpmatch[j] * dpmatch[j];
                            ret %= mod;
                            ret += mod;
                            ret %= mod;
                        }

                        ret *= C[N - i - 1, j + j - 1]; // 数字の選び方
                        ret %= mod;

                        ret *= kaijo[j + j - 1];
                        ret %= mod;
                        ret *= rev(j);
                        ret %= mod;
                        dp[i + j * 2] += dp[i] * ret;
                        dp[i + j * 2] %= mod;
                        //Console.WriteLine("double : " + i + " " + j + " " + ret);
                    }
                    //Console.WriteLine(i + " " + dp[i]);
                }
                Console.WriteLine(dp[N]);
            }
        }
        else
        {
            long ret = dpmatch[N];
            for (int i = 0; i < N; i++)
            {
                ret *= N;
                ret %= mod;
            }
            Console.WriteLine(ret);
        }
    }

    long[] dpmatch;
    void match(int MAX)
    {
        dpmatch = new long[MAX + 1];
        for (int j = 0; j < MAX; j++)
        {
            long[] dpmatch2 = new long[MAX];
            dpmatch2[0] = 1;
            for (int i = 0; i < j; i++)
            {
                dpmatch2[i + 1] += dpmatch2[i];
                dpmatch2[i + 1] %= mod;
                if (i != j - 1)
                {
                    dpmatch2[i + 2] += dpmatch2[i] * (j - i - 1);
                    dpmatch2[i + 2] %= mod;
                }
            }
            dpmatch[j] = dpmatch2[j];
        }
    }

}




class Scanner
{
    string[] s;
    int i;

    char[] cs = new char[] { ' ' };

    public Scanner()
    {
        s = new string[0];
        i = 0;
    }

    public string next()
    {
        if (i < s.Length) return s[i++];
        do
        {
            s = Console.ReadLine().Split(cs, StringSplitOptions.RemoveEmptyEntries);
        } while ((s.Length == 1 && s[0] == "") || s.Length == 0);
        i = 0;
        return s[i++];
    }

    public int nextInt()
    {
        return int.Parse(next());
    }

    public long nextLong()
    {
        return long.Parse(next());
    }

    public double nextDouble()
    {
        return double.Parse(next());
    }

}