Problem:
Score : 300 points

Problem Statement

Snuke has N dogs and M monkeys. He wants them to line up in a row.

As a Japanese saying goes, these dogs and monkeys are on bad terms. ("ken'en no naka", literally "the relationship of dogs and monkeys", means a relationship of mutual hatred.) Snuke is trying to reconsile them, by arranging the animals so that there are neither two adjacent dogs nor two adjacent monkeys.

How many such arrangements there are? Find the count modulo 10^9+7 (since animals cannot understand numbers larger than that).
Here, dogs and monkeys are both distinguishable. Also, two arrangements that result from reversing each other are distinguished.

Constraints

1 ≤ N,M ≤ 10^5

Input

Input is given from Standard Input in the following format:

N M

Output

Print the number of possible arrangements, modulo 10^9+7.

Sample Input 1

2 2

Sample Output 1

8

We will denote the dogs by A and B, and the monkeys by C and D. There are eight possible arrangements: ACBD, ADBC, BCAD, BDAC, CADB, CBDA, DACB and DBCA.

Sample Input 2

3 2

Sample Output 2

12

Sample Input 3

1 8

Sample Output 3

0

Sample Input 4

100000 100000

Sample Output 4

530123477

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 ARC076C2
{
    class Program
    {
        public static void Solve(Input input)
        {
            var n = input.NextInt();
            var m = input.NextInt();

            if (Math.Abs(m - n) >= 2)
            {
                // 隣り合わない組み合わせが存在しない
                Console.WriteLine(0);
                return;
            }

            // nPn * mPm
            var ans = new ModInt(1);

            // nPn = n!
            ans *= ModInt.Factorial(n, MOD);
            ans *= ModInt.Factorial(m, MOD);
            if (n == m)
            {
                // 同じ数だけいるなら、どちらを先頭とするかで組み合わせが増える
                ans *= 2;
            }

            Console.WriteLine(ans);
        }

        /// <summary>
        /// Mを法とする数値
        /// Mは素数
        /// </summary>
        public struct ModInt
        {
            public readonly int M;
            private readonly int N;

            /// <summary>
            /// 
            /// </summary>
            /// <param name="n">実際の数値</param>
            /// <param name="mod">M</param>
            public ModInt(int n, int mod = (int)1e9 + 7)
            {
                N = n % mod;
                M = mod;
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt Add(int a, int b, int M)
                => new ModInt((a + b) % M, M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt Sub(int a, int b, int M)
                => new ModInt((a + M - b) % M, M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt Mul(int a, int b, int M)
                => new ModInt((int)(((long)(a % M) * (b % M)) % M), M);

            /// <summary> a/b </summary>
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt Div(int a, int b, int M)
                => Mul(a, Inverse(b, M), M);

            /// <summary>
            /// aの逆元（a^-1）
            /// （厳密には違うが）Mは素数とする
            /// </summary>
            public static ModInt Inverse(int a, int M)
            {
                int b = 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 += M;

                return new ModInt(u, M);
            }

            /// <summary>
            /// x^y
            /// 繰り返し二乗法
            /// </summary>
            public static ModInt Power(int a, int b, int M)
            {
                var r = new ModInt(1, M);

                while (b > 0)
                {
                    if ((b & 1) != 0) r = Mul(r, a, M);

                    a = Mul(a, a, M);
                    b >>= 1;
                }

                return r;
            }

            public override string ToString() => N.ToString();

            #region Combinatorics
            /// <summary>
            /// 階乗(n!)
            /// </summary>
            public static ModInt Factorial(int n, int M)
            {
                var fact = new ModInt(1, M);
                while (n > 0)
                {
                    fact *= n;
                    n--;
                }

                return fact;
            }

            /// <summary>
            /// 順列(nPr)
            /// </summary>
            public static ModInt Npr(int n, int r, int M)
            {
                if (n < r || n < 0 || r < 0)
                {
                    return new ModInt(0, M);
                }

                var npr = new ModInt(1, M);
                for (int i = n; i > n - r; i--)
                {
                    npr *= n;
                }

                return npr;
            }

            /// <summary>
            /// 組み合わせ(nCr)
            /// </summary>
            public static ModInt Ncr(int n, int r, int M)
            {
                if (n < r || n < 0 || r < 0)
                {
                    return new ModInt(0, M);
                }

                r = Math.Min(r, n - r);
                var npr = Npr(n, r, M);
                var fact = Factorial(r, M);

                return npr / fact;
            }

            /// <summary>
            /// 重複組み合わせ(nHr)
            /// n個から重複を許してr個取り出す
            /// </summary>
            public static ModInt Nhr(int n, int r, int M)
                => Ncr(n + r - 1, r, M);
            #endregion

            #region Operator, Alias
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static implicit operator int(ModInt a)
                => a.N;

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator +(ModInt a, int b)
                => Add(a, b, a.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator -(ModInt a, int b)
                => Sub(a, b, a.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator *(ModInt a, int b)
                => Mul(a, b, a.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator /(ModInt a, int b)
                => Div(a, b, a.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator +(int a, ModInt b)
                => Add(a, b, b.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator -(int a, ModInt b)
                => Sub(a, b, b.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator *(int a, ModInt b)
                => Mul(a, b, b.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator /(int a, ModInt b)
                => Div(a, b, b.M);

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator +(ModInt a, ModInt b)
            {
                if (a.M != b.M) throw new ArgumentException();
                return Add(a, b, a.M);
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator -(ModInt a, ModInt b)
            {
                if (a.M != b.M) throw new ArgumentException();
                return Sub(a, b, a.M);
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator *(ModInt a, ModInt b)
            {
                if (a.M != b.M) throw new ArgumentException();
                return Mul(a, b, a.M);
            }

            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt operator /(ModInt a, ModInt b)
            {
                if (a.M != b.M) throw new ArgumentException();
                return Div(a, b, a.M);
            }

            /// <summary> a^b </summary>
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt Power(ModInt a, int b)
                => Power(a, b, a.M);

            /// <summary> a^b </summary>
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt Power(ModInt a, ModInt b)
            {
                if (a.M != b.M) throw new ArgumentException();
                return Power(a, b, a.M);
            }

            /// <summary> n! </summary>
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            public static ModInt Factorial(ModInt n)
                => Factorial(n, n.M);

            #endregion

            #region Misc
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
            static void Swap<T>(ref T a, ref T b)
            {
                T temp = a;
                a = b;
                b = temp;
            }
            #endregion
        }

        #region Main
        public static void Main(string[] args)
        {
            // 出力が少ないときはこれをセットする方が時間かかるけれど
            // そのときにTLEするのはアルゴリズムが悪いせいだし、まあ良しとする
            var needsFlushOutput = true;
            if (needsFlushOutput)
            {
                // 細かく出力しないようにする
                var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
                Console.SetOut(sw);
            }

            // 通常は引数無しで、ファイルから読み出したい場合はpath指定する
            var input = new Input();

            // 仮想的に標準入力をセットする
            // テスト入力したまま提出することがままあるので……
#if DEBUG
            input.SetText("");
#endif

            Solve(input);

            Console.Out.Flush();
        }
        #endregion

        #region Competitive Template
#pragma warning disable CS0414
        static readonly int MOD = (int)1e9 + 7;
        static readonly int[] dx = { 1, 0, -1, 0 };
        static readonly int[] dy = { 0, 1, 0, -1 };
        /// <summary> 左上が原点 </summary>
        static readonly char[] dir = { 'R', 'U', 'L', 'D' };
#pragma warning restore CS0414

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

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

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

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

            /// <summary>
            /// 入力予約
            /// </summary>
            public bool SetText(string s)
            {
                if (string.IsNullOrEmpty(s)) return false;

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

                return true;
            }

            /// <summary>
            /// 要素が存在するか
            /// </summary>
            public bool Any() => queue.Any() || Read();

            /// <summary>
            /// 内部queueに入力からの値をsplitして格納する
            /// </summary>
            bool Read()
            {
                if (!SetText(sr.ReadLine())) return false;

                if (!queue.Any()) return Read();

                return queue.Any();
            }

            /// <summary>
            /// 次のstringを一つ読み込む
            /// </summary>
            public string Next()
            {
                if (!queue.Any() && !Read()) return "";

                return queue.Dequeue();
            }

            /// <summary>
            /// 指定個数queueにたまるまでenqueueし続ける
            /// </summary>
            bool Accumulate(int n)
            {
                while (queue.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>
            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(queue.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 T Max<T>(params T[] objs) => objs.Max();
            public static T Min<T>(params T[] objs) => objs.Min();

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

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