AtCoderのABC    次のABCの問題へ    前のABCの問題へ

ABC132-D Blue and Red Balls


問題へのリンク


C#のソース

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

class Program
{
    static string InputPattern = "InputX";

    static List<string> GetInputList()
    {
        var WillReturn = new List<string>();

        if (InputPattern == "Input1") {
            WillReturn.Add("5 3");
            //3
            //6
            //1
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("2000 3");
            //1998
            //3990006
            //327341989
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    const long Hou = 1000000007;

    static void Main()
    {
        List<string> InputList = GetInputList();

        long[] wkArr = InputList[0].Split(' ').Select(pX => long.Parse(pX)).ToArray();

        long N = wkArr[0];
        long K = wkArr[1];

        long BlueCnt = K;
        long RedCnt = N - K;

        for (long I = 1; I <= BlueCnt; I++) {
            long Answer = 0;

            // 青をI個の固まりに分ける場合の数
            long BluePattern = DeriveKatamariPattern(BlueCnt, I);

            // 赤をI-1個の固まりに分ける場合の数
            long RedPattern1 = DeriveKatamariPattern(RedCnt, I - 1);

            // 赤をI個の固まりに分ける場合の数
            long RedPattern2 = DeriveKatamariPattern(RedCnt, I);
            // 2つの場合があるので2倍する
            RedPattern2 *= 2;
            RedPattern2 %= Hou;

            // 赤をI+1個の固まりに分ける場合の数
            long RedPattern3 = DeriveKatamariPattern(RedCnt, I + 1);

            Answer += (BluePattern * RedPattern1) % Hou;
            Answer %= Hou;
            Answer += (BluePattern * RedPattern2) % Hou;
            Answer %= Hou;
            Answer += (BluePattern * RedPattern3) % Hou;
            Answer %= Hou;

            Console.WriteLine(Answer);
        }
    }

    // X個をY個の固まりに分ける場合の数を返す
    static long DeriveKatamariPattern(long pX, long pY)
    {
        // 0個を、0個の固まるに分けるのは、1通り
        if (pX == 0 && pY == 0) return 1;

        // 0個より多い場合は、0個の固まりにできないので、0通り
        if (pY == 0) return 0;

        // 個数より固まりの数が多い場合は、0通り
        if (pX < pY) return 0;

        // 先に1個ずつ分配する
        long Rest = pX - pY;

        if (Rest == 0) return 1;

        // 重複組み合わせの、○と縦棒の考え方を使う
        long TatebouCnt = pY - 1;
        long SumCnt = Rest + TatebouCnt;

        return DeriveCombi(SumCnt, Rest);
    }

    // nCr mod 法 を求める
    static long DeriveCombi(long pN, long pR)
    {
        pR = Math.Min(pR, pN - pR);

        long WillReturn = 1;
        for (long I = pN; pN - pR < I; I--) {
            WillReturn *= I;
            WillReturn %= Hou;
        }
        for (long I = 2; I <= pR; I++) {
            WillReturn *= DeriveGyakugen(I);
            WillReturn %= Hou;
        }
        return WillReturn;
    }

    //引数の逆元を求める
    static long DeriveGyakugen(long pLong)
    {
        return DeriveBekijyou(pLong, Hou - 2, Hou);
    }

    //繰り返し2乗法で、(NのP乗) Mod Mを求める
    static long DeriveBekijyou(long pN, long pP, long pM)
    {
        long CurrJyousuu = pN % pM;
        long CurrShisuu = 1;
        long WillReturn = 1;

        while (true) {
            //対象ビットが立っている場合
            if ((pP & CurrShisuu) > 0) {
                WillReturn = (WillReturn * CurrJyousuu) % pM;
            }

            CurrShisuu *= 2;
            if (CurrShisuu > pP) return WillReturn;
            CurrJyousuu = (CurrJyousuu * CurrJyousuu) % pM;
        }
    }
}


解説

何個かの固まりに分ける場合の数は、
重複組み合わせの○と縦棒の考え方を使って、求めてます。