AOJ本の読書メモ   AOJ    次のAOJの問題へ    前のAOJの問題へ

AOJ 0626 スタンプラリー2


問題へのリンク


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");
            WillReturn.Add("JOIOI");
            //6
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("7");
            WillReturn.Add("JJJOIII");
            //18
        }
        else if (InputPattern == "Input3") {
            WillReturn.Add("4");
            WillReturn.Add("OIIJ");
            //2
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    static void Main()
    {
        List<string> InputList = GetInputList();
        string S = InputList[1];
        int UB = S.Length - 1;

        var AnswerList = new List<long>();

        // Jは、1番左に追加が最適
        AnswerList.Add(ExecDP("J" + S));

        // Iは、1番右に追加が最適
        AnswerList.Add(ExecDP(S + "I"));

        // Oを追加すると、左のJの数 * 右のIの数だけ解が増える
        long BaseCnt = ExecDP(S);

        // Jの数のフェニック木
        var InsFenwick_Tree_J = new Fenwick_Tree(UB);

        // Iの数のフェニック木
        var InsFenwick_Tree_I = new Fenwick_Tree(UB);

        for (int I = 0; I <= UB; I++) {
            if (S[I] == 'J') InsFenwick_Tree_J.Add(I, 1);
            if (S[I] == 'I') InsFenwick_Tree_I.Add(I, 1);
        }

        for (int I = 0; I <= UB - 1; I++) {
            int LeftRangeSta = 0;
            int LeftRangeEnd = I;

            int RightRangeSta = I + 1;
            int RightRangeEnd = UB;

            long Cnt_J = InsFenwick_Tree_J.GetSum(LeftRangeSta, LeftRangeEnd);
            long Cnt_I = InsFenwick_Tree_I.GetSum(RightRangeSta, RightRangeEnd);

            long ProdVal = Cnt_J * Cnt_I;
            AnswerList.Add(BaseCnt + ProdVal);
        }
        Console.WriteLine(AnswerList.Max());
    }

    // 文字列を引数として、場合の数を返す
    static long ExecDP(string pStr)
    {
        // 場合の数[状態]
        // 0 なにもなし
        // 1 J
        // 2 O
        long UB = 2;
        long[] DPArr = new long[UB + 1];
        DPArr[0] = 1;

        long Answer = 0;
        foreach (char EachChar in pStr) {
            for (long I = UB; 0 <= I; I--) {
                if (DPArr[I] == 0) continue;

                if (I == 0 && EachChar == 'J') {
                    DPArr[1] += DPArr[I];
                }
                if (I == 1 && EachChar == 'O') {
                    DPArr[2] += DPArr[I];
                }
                if (I == 2 && EachChar == 'I') {
                    Answer += DPArr[I];
                }
            }
        }
        return Answer;
    }
}

// フェニック木
#region Fenwick_Tree
internal class Fenwick_Tree
{
    private long[] mBitArr;
    private long mExternalArrUB;

    // ノードのIndexの列挙を返す
    internal IEnumerable<long> GetNodeIndEnum()
    {
        for (long I = 0; I <= mExternalArrUB; I++) {
            yield return I;
        }
    }

    // 木のノードのUBを返す
    internal long GetUB()
    {
        return mExternalArrUB;
    }

    // コンストラクタ(外部配列のUBのみ指定)
    internal Fenwick_Tree(long pExternalArrUB)
    {
        mExternalArrUB = pExternalArrUB;

        // フェニック木の外部配列は0オリジンで、
        // フェニック木の内部配列は1オリジンなため、2を足す
        mBitArr = new long[pExternalArrUB + 2];
    }

    // コンストラクタ(初期化用の配列指定)
    internal Fenwick_Tree(long[] pArr)
        : this(pArr.GetUpperBound(0))
    {
        for (long I = 0; I <= pArr.GetUpperBound(0); I++) {
            this.Add(I, pArr[I]);
        }
    }

    // コンストラクタ(初期化用のList指定)
    internal Fenwick_Tree(List<long> pList)
        : this(pList.Count - 1)
    {
        for (int I = 0; I <= pList.Count - 1; I++) {
            this.Add(I, pList[I]);
        }
    }

    // [pSta,pEnd] のSumを返す
    internal long GetSum(long pSta, long pEnd)
    {
        return GetSum(pEnd) - GetSum(pSta - 1);
    }

    // [0,pEnd] のSumを返す
    internal long GetSum(long pEnd)
    {
        pEnd++; // 1オリジンに変更

        long Sum = 0;
        while (pEnd >= 1) {
            Sum += mBitArr[pEnd];
            pEnd -= pEnd & -pEnd;
        }
        return Sum;
    }

    // [I] に Xを加算
    internal void Add(long pI, long pX)
    {
        pI++; // 1オリジンに変更

        while (pI <= mBitArr.GetUpperBound(0)) {
            mBitArr[pI] += pX;
            pI += pI & -pI;
        }
    }
}
#endregion


解説

Jを追加する場合は、1番左に追加するのが最適です。
Iを追加する場合は、1番右に追加するのが最適です。
Oを追加する場合は、積の法則で、(左のJの数) * (右のJの数)だけ増えるので、
この増加量が、最大の箇所に追加するのが最適です。

以上により、追加する文字を全て調べれば解けます。