AtCoderのARC    次のARCの問題へ    前のARCの問題へ

ARC112-B -- - B


問題へのリンク


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("11 2");
            //3
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("0 4");
            //4
        }
        else if (InputPattern == "Input3") {
            WillReturn.Add("112 20210213");
            //20210436
        }
        else if (InputPattern == "Input4") {
            WillReturn.Add("-211 1000000000000000000");
            //1000000000000000422
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    static void Main()
    {
        List<string> InputList = GetInputList();
        long[] wkArr = InputList[0].Split(' ').Select(pX => long.Parse(pX)).ToArray();
        long B = wkArr[0];
        long C = wkArr[1];

        long Result = Solve(B, C);
        Console.WriteLine(Result);
    }

    static void Debugger()
    {
        //long Result1 = Solve(-2, 4);
        //long Result2 = SolveDFS(-2, 4);
        //if (Result1 != Result2) {
        //    Console.WriteLine("■■■差異あり■■■");
        //    Console.WriteLine("Solve={0}", Result1);
        //    Console.WriteLine("SolveDFS={0}", Result2);
        //}

        var InsRandom = new Random();

        for (int I = 1; I <= 30; I++) {
            long RandB = InsRandom.Next(-99, 99);
            long RandC = InsRandom.Next(5, 100);

            long Result1 = Solve(RandB, RandC);
            long Result2 = SolveDFS(RandB, RandC);
            if (Result1 != Result2) {
                Console.WriteLine("差異あり");
                Console.WriteLine("B={0},C={1}", RandB, RandC);
                Console.WriteLine("Solve={0}", Result1);
                Console.WriteLine("SolveDFS={0}", Result2);
            }
        }
    }

    static long Solve(long pB, long pC)
    {
        // コーナーケース回避で、Cが100以下だったら、DFSで解く
        if (pC <= 100) {
            return SolveDFS(pB, pC);
        }

        // 場合1 Bが0以上の場合
        if (pB >= 0) {
            return SolvePlus(pB, pC);
        }
        // 場合2 Bが負の場合
        return SolveMinus(pB, pC);
    }

    // 閉区間の定義
    class RangeInfoDef
    {
        internal long Sta;
        internal long End;
    }

    // Bが正の場合
    static long SolvePlus(long pB, long pC)
    {
        var RangeInfoList = new List<RangeInfoDef>();

        // 閉区間1 ひたすら1を引く
        RangeInfoDef Range1 = new RangeInfoDef();
        Range1.Sta = pB;
        Range1.End = pB - pC / 2;
        RangeInfoList.Add(Range1);

        // 閉区間2 閉区間1の-1倍で到達可能な数値の区間
        RangeInfoDef Range2 = new RangeInfoDef();
        Range2.Sta = Range1.Sta;
        Range2.End = Range1.End;
        long RestC = pC % 2;
        if (RestC == 0) {
            Range2.End++;
        }
        Range2.Sta *= -1;
        Range2.End *= -1;
        RangeInfoList.Add(Range2);

        // 閉区間3 -1倍してから、ひたすら1を引く
        RangeInfoDef Range3 = new RangeInfoDef();
        Range3.Sta = -pB;
        Range3.End = -pB - (pC - 1) / 2;
        RangeInfoList.Add(Range3);

        // 閉区間4 閉区間3の-1倍で到達可能な数値の区間
        RangeInfoDef Range4 = new RangeInfoDef();
        Range4.Sta = Range3.Sta;
        Range4.End = Range3.End;
        RestC = (pC - 1) % 2;
        if (RestC == 0) {
            Range4.End++;
        }
        Range4.Sta *= -1;
        Range4.End *= -1;
        RangeInfoList.Add(Range4);

        return DeriveUniqNumCnt(RangeInfoList);
    }

    // Bが負の場合
    static long SolveMinus(long pB, long pC)
    {
        var RangeInfoList = new List<RangeInfoDef>();

        // 閉区間1 ひたすら1を引く
        RangeInfoDef Range1 = new RangeInfoDef();
        Range1.Sta = pB;
        Range1.End = pB - pC / 2;
        RangeInfoList.Add(Range1);

        // 閉区間2 閉区間1に-1倍で到達可能な数値の区間
        RangeInfoDef Range2 = new RangeInfoDef();
        Range2.Sta = Range1.Sta;
        Range2.End = Range1.End;
        long RestC = pC % 2;
        if (RestC == 0) {
            Range2.End++;
        }
        Range2.Sta *= -1;
        Range2.End *= -1;
        RangeInfoList.Add(Range2);

        // 閉区間3 -1倍してから、ひたすら1を引く
        RangeInfoDef Range3 = new RangeInfoDef();
        Range3.Sta = -pB;
        Range3.End = -pB - (pC - 1) / 2;
        RangeInfoList.Add(Range3);

        // 閉区間4 閉区間3の-1倍で到達可能な数値の区間
        RangeInfoDef Range4 = new RangeInfoDef();
        Range4.Sta = Range3.Sta;
        Range4.End = Range3.End;
        RestC = (pC - 1) % 2;
        if (RestC == 0) {
            Range4.End++;
        }
        Range4.Sta *= -1;
        Range4.End *= -1;
        RangeInfoList.Add(Range4);

        return DeriveUniqNumCnt(RangeInfoList);
    }

    // 閉区間のListを引数として、何通りの数があるかを返す
    static long DeriveUniqNumCnt(List<RangeInfoDef> pRangeInfoList)
    {
        // 開始 <= 終了 にする
        for (int I = 0; I <= pRangeInfoList.Count - 1; I++) {
            long Sta = pRangeInfoList[I].Sta;
            long End = pRangeInfoList[I].End;
            if (Sta > End) {
                pRangeInfoList[I].Sta = End;
                pRangeInfoList[I].End = Sta;
            }
        }

        //Console.WriteLine("マージ前の区間");
        //foreach (RangeInfoDef EachRangeInfo in pRangeInfoList) {
        //    Console.WriteLine("区間開始{0} 区間終了{1}", EachRangeInfo.Sta, EachRangeInfo.End);
        //}

        for (int I = pRangeInfoList.Count - 1; 1 <= I; I--) {
            long CurrSta = pRangeInfoList[I].Sta;
            long CurrEnd = pRangeInfoList[I].End;

            for (int J = I - 1; 0 <= J; J--) {
                long PrevSta = pRangeInfoList[J].Sta;
                long PrevEnd = pRangeInfoList[J].End;

                // OverLapしてたら、マージしてから、Remove
                if (PrevSta <= CurrEnd && CurrSta <= PrevEnd) {
                    pRangeInfoList[J].Sta = Math.Min(CurrSta, PrevSta);
                    pRangeInfoList[J].End = Math.Max(CurrEnd, PrevEnd);

                    pRangeInfoList.RemoveAt(I);
                    break;
                }
            }
        }

        //Console.WriteLine("マージ後の区間");
        //foreach (RangeInfoDef EachRangeInfo in pRangeInfoList) {
        //    Console.WriteLine("区間開始{0} 区間終了{1}", EachRangeInfo.Sta, EachRangeInfo.End);
        //}
        return pRangeInfoList.Sum(pX => pX.End - pX.Sta + 1);
    }

    struct JyoutaiDef
    {
        internal long CurrPos;
        internal long RevCnt;
        internal long Cost;
    }

    static long SolveDFS(long pB, long pC)
    {
        var Stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        WillPush.CurrPos = pB;
        WillPush.RevCnt = 0;
        WillPush.Cost = 0;
        Stk.Push(WillPush);

        var MinCostDict = new Dictionary<long, long>();
        MinCostDict[pB] = 0;
        while (Stk.Count > 0) {
            JyoutaiDef Popped = Stk.Pop();

            var PushKouhoList = new List<JyoutaiDef>();

            // -1倍する場合
            if (Popped.RevCnt < 3) {
                WillPush.CurrPos = Popped.CurrPos * (-1);
                WillPush.RevCnt = Popped.RevCnt + 1;
                WillPush.Cost = Popped.Cost + 1;
                PushKouhoList.Add(WillPush);
            }

            // -1する場合
            WillPush.CurrPos = Popped.CurrPos - 1;
            WillPush.RevCnt = Popped.RevCnt;
            WillPush.Cost = Popped.Cost + 2;
            PushKouhoList.Add(WillPush);

            foreach (JyoutaiDef EachPushKouho in PushKouhoList) {
                if (EachPushKouho.Cost > pC) continue;

                if (MinCostDict.ContainsKey(EachPushKouho.CurrPos)) {
                    if (MinCostDict[EachPushKouho.CurrPos] <= EachPushKouho.Cost) {
                        continue;
                    }
                }
                MinCostDict[EachPushKouho.CurrPos] = EachPushKouho.Cost;
                Stk.Push(EachPushKouho);
            }
        }
        //foreach (var EachPair in MinCostDict) {
        //    Console.WriteLine("MinCostDict[{0}]={1}", EachPair.Key, EachPair.Value);
        //}
        return MinCostDict.Count;
    }
}


解説

Cが100以下ならDFSでナイーブに解いてます。

Cが100より大きい場合は、Bの符号で場合分けしつつ、
移動パターンごとの閉区間を調べて、最後にOverLapした閉区間をマージしてます。