AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC115-D Christmas
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("2 7");
//4
}
else if (InputPattern == "Input2") {
WillReturn.Add("1 1");
//0
}
else if (InputPattern == "Input3") {
WillReturn.Add("50 4321098765432109");
//2160549382716056
}
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 N = wkArr[0];
long X = wkArr[1];
long Result = Solve(N, X);
Console.WriteLine(Result);
}
struct CountInfo
{
internal long CntP;
internal long CntB;
internal long TotalLen;
}
static List<CountInfo> mCountInfoList = new List<CountInfo>();
static long Solve(long pN, long pX)
{
long CntP = 1;
long CntB = 0;
CountInfo WillAdd;
WillAdd.CntP = CntP;
WillAdd.CntB = CntB;
WillAdd.TotalLen = CntP + CntB;
mCountInfoList.Add(WillAdd);
for (long I = 1; I <= pN; I++) {
CntP = 2 * CntP + 1;
CntB = 2 * CntB + 2;
WillAdd.CntP = CntP;
WillAdd.CntB = CntB;
WillAdd.TotalLen = CntP + CntB;
mCountInfoList.Add(WillAdd);
}
long Result = Rec(pN, pX);
return Result;
}
// パティのレベルと、何文字目までを数えるかを引数として、Pの数を返す
static long Rec(long pLevel, long pLimitLen)
{
if (pLimitLen == 0) return 0;
// 完全に区間をカバーする場合
CountInfo CurrCountInfo = mCountInfoList[(int)pLevel];
if (pLimitLen >= CurrCountInfo.TotalLen) {
return CurrCountInfo.CntP;
}
// 一部の区間が一致する場合
var PCntList = new List<long>();
CountInfo PrevCountInfo = mCountInfoList[(int)pLevel - 1];
pLimitLen--; // 最初のBの分を引く
PCntList.Add(Rec(pLevel - 1, pLimitLen));
long Rest = pLimitLen - PrevCountInfo.TotalLen;
if (Rest > 0) {
PCntList.Add(1); // 真ん中のPの分
Rest--;
}
if (Rest > 0) {
PCntList.Add(Rec(pLevel - 1, Rest));
}
return PCntList.Sum();
}
}
解説
遅延でないセグ木の区間Sumや区間Minの取得のような感覚で
再帰を使ってます。