Problem:
Score : 400 points

Problem Statement

There is a knight - the chess piece - at the origin (0, 0) of a two-dimensional grid.

When the knight is at the square (i, j), it can be moved to either (i+1,j+2) or (i+2, j+1).

In how many ways can the knight reach the square (X, Y)?

Find the number of ways modulo 10^9 + 7.

Constraints

1 \leq X \leq 10^6

1 \leq Y \leq 10^6

All values in input are integers.

Input

Input is given from Standard Input in the following format:

X Y

Output

Print the number of ways for the knight to reach (X, Y) from (0, 0), modulo 10^9 + 7.

Sample Input 1

3 3

Sample Output 1

2

There are two ways: (0,0) \to (1,2) \to (3,3) and (0,0) \to (2,1) \to (3,3).

Sample Input 2

2 2

Sample Output 2

0

The knight cannot reach (2,2).

Sample Input 3

999999 999999

Sample Output 3

151840682

Print the number of ways modulo 10^9 + 7.

Solution in C# (Mono-mcs 6.8.0.105) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Windows;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Atcoder
{
    public class Program
    {
        private static long mod = 1000000007;
        private static long max = 999999999999;

        static void Main(string[] args)
        {
            var r = ReadSplit();
            var x = r[0].Int();
            var y = r[1].Int();
            var k = (x + y) / 3;
            if ((x + y) % 3 != 0
                || (2 * x - y) % 3 != 0
                || (2 * y - x) % 3 != 0)
            {
                WriteRead(0);
                return;
            }
            int xm = (2 * x - y) / 3;
            int ym = (2 * y - x) / 3;
            var com = new ModCombination();
            var ans = com.Combination(k, xm);
            WriteRead(ans);
        }

        public static List<long> GetCalcDivisor(long n)
        {
            var res = new List<long>();
            for (long i = 1L; i * i <= n; ++i)
            {
                if (n % i == 0)
                {
                    res.Add(i);
                    long j = n / i;
                    if (j != i) res.Add(j);
                }
            }
            res.Sort();
            return res;
        }

        class ModCombination
        {
            public long _mod = 1000000007;
            public int _max = 1000000;

            long[] fac;
            long[] finv;
            long[] inv;

            public ModCombination() : this(1000000007, 1000000)
            {
            }

            public ModCombination(long mod, int max)
            {
                _mod = mod;
                _max = max;
                fac = new long[max];
                finv = new long[max];
                inv = new long[max];
                fac[0] = fac[1] = 1;
                finv[0] = finv[1] = 1;
                inv[1] = 1;
                for (int i = 2; i < max; i++)
                {
                    fac[i] = fac[i - 1] * i % mod;
                    inv[i] = mod - inv[mod % i] * (mod / i) % mod;
                    finv[i] = finv[i - 1] * inv[i] % mod;
                }
            }

            public long Combination(int n, int m)
            {
                if (n < m) return 0;
                if (n < 0 || m < 0) return 0;
                return fac[n] * (finv[m] * finv[n - m] % _mod) % _mod;
            }
        }

        public static string Read()
        {
            return Console.ReadLine();
        }

        public static string[] ReadSplit()
        {
            return Console.ReadLine().Split(' ');
        }

        public static void WriteRead(object str)
        {
            Console.WriteLine(str);
            Read();
        }

        public static void Write(object str)
        {
            Console.WriteLine(str);
        }

        private static bool ChangeMin<T>(ref T a, T b) where T : IComparable
        {
            if (a.CompareTo(b) > 0)
            {
                a = b;
                return true;
            }
            return false;
        }

        private static bool ChangekMax<T>(ref T a, T b) where T : IComparable
        {
            if (b.CompareTo(a) > 0)
            {
                a = b;
                return true;
            }
            return false;
        }

        /*優先度付きキュー*/
        class PriorityQueue<T> where T : IComparable<T>
        {
            public readonly T[] Array;
            public readonly bool Greater;
            public int Count { get; private set; } = 0;

            public PriorityQueue(int capacity, bool greater = true)
            {
                this.Array = new T[capacity];
                this.Greater = greater;
            }

            public void Enqueue(T item)
            {
                this.Array[this.Count] = item;
                this.Count += 1;
                this.UpHeap();
            }

            public T Dequeue()
            {
                // 先頭要素を末尾にして再構成
                this.Swap(0, this.Count - 1);
                this.Count -= 1;
                this.DownHeap();

                return this.Array[this.Count];
            }


            private void UpHeap()
            {
                var n = this.Count - 1;
                while (n != 0)
                {
                    // 親要素の座標
                    var parent = (n - 1) / 2;

                    if (this.Compare(this.Array[n], this.Array[parent]) > 0)
                    {
                        this.Swap(n, parent);
                        n = parent;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            private void DownHeap()
            {
                var parent = 0;
                while (true)
                {
                    var child = 2 * parent + 1;
                    if (child > this.Count - 1) break;

                    if (child < this.Count - 1 && this.Compare(this.Array[child], this.Array[child + 1]) < 0)
                    {
                        // 左より右の子のほうが大きい値の場合、右を入れ替え対象にする
                        child += 1;
                    }

                    if (this.Compare(this.Array[parent], this.Array[child]) < 0)
                    {
                        this.Swap(parent, child);
                        parent = child;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            //aとbを比較する
            private int Compare(T a, T b)
            {
                var c = a.CompareTo(b);
                if (!this.Greater)
                {
                    c = -c;
                }
                return c;
            }

            //aとbを入れ替える
            private void Swap(int a, int b)
            {
                var tmp = this.Array[a];
                this.Array[a] = this.Array[b];
                this.Array[b] = tmp;
            }
        }

        private static bool NextPermutation(int[] array)
        {
            var size = array.Length;
            var ok = false;

            //array[i]<array[i+1]を満たす最大のiを求める
            int i = size - 2;
            for (; 0 <= i; i--)
            {
                if (array[i] < array[i + 1])
                {
                    ok = true;
                    break;
                }
            }

            //全ての要素が降順の場合、NextPermutationは存在しない
            if (ok == false) return false;

            //array[i]<array[j]を満たす最大のjを求める
            int j = size - 1;
            for (; ; j--)
            {
                if (array[i] < array[j]) break;
            }

            //iとjの要素をswapする
            var tmp = array[i];
            array[i] = array[j];
            array[j] = tmp;

            //i以降の要素を反転させる
            Array.Reverse(array, i + 1, size - (i + 1));

            return true;
        }

        //要素数Nを引数として渡し、0～N-1でできた順列をすべて返す
        static List<int[]> GetAllPermutation(int N)
        {
            var res = new List<int[]>();
            var array = Enumerable.Range(0, N).ToArray();
            do
            {
                var copy = new int[N];
                array.CopyTo(copy, 0);
                res.Add(copy);
            } while (NextPermutation(array));

            return res;
        }

        //配列arrayを引数として渡し,辞書順がarray以上の順列をすべて返す
        static List<int[]> GetAllPermutation(int[] array)
        {
            var size = array.Length;
            var res = new List<int[]>();
            do
            {
                var copy = new int[size];
                array.CopyTo(copy, 0);
                res.Add(copy);
            } while (NextPermutation(array));

            return res;
        }

        public static long ModPow(long x, long n)
        {
            x = x % mod;
            if (n == 0)
            {
                return 1;
            }
            else if (n % 2 == 1)
            {
                return (x * ModPow(x, n - 1)) % mod;
            }
            else
            {
                return ModPow((x * x) % mod, n / 2) % mod;
            }
        }

        /// <summary>
        /// 最小公倍数
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static long Lcm(long a, long b)
        {
            return a / Gcd(a, b) * b;
        }
        /// <summary>
        /// 最大公約数
        /// </summary>
        /// <param name="m"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public static long Gcd(long m, long n)
        {
            if (n == 0) return m;
            return Gcd(n, m % n);
        }
    }
}


public static class Extentions
{
    public static int Int(this object obj)
    {
        return int.Parse(obj.ToString());
    }

    public static long Long(this object obj)
    {
        return long.Parse(obj.ToString());
    }

    public static decimal Decimal(this object obj)
    {
        return decimal.Parse(obj.ToString());
    }

    public static double Double(this object obj)
    {
        return double.Parse(obj.ToString());
    }

    public static T2 GetValueZero<T1, T2>(this Dictionary<T1, T2> d, T1 key) where T2 : struct
    {
        T2 value;
        if (d.TryGetValue(key, out value))
        {
            return value;
        }
        return new T2();
    }
}