AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC424-E Cut in Half
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");
WillReturn.Add("3 4 5");
WillReturn.Add("40 20 30");
WillReturn.Add("10 100 50");
WillReturn.Add("1 2 3 4 5 6 7 8 9 10");
//10.00000000000000000000
//0.50000000000000000000
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => long.Parse(pX)).ToArray();
}
static long mGeta = 1L << 32;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = { };
Action<string> SplitAct = (pStr) => wkArr = GetSplitArr(pStr);
var sb = new System.Text.StringBuilder();
for (int I = 1; I <= InputList.Count - 1; I += 2) {
SplitAct(InputList[I]);
mK = wkArr[1];
mX = wkArr[2];
mAArr = GetSplitArr(InputList[I + 1]);
UB = mAArr.GetUpperBound(0);
for (long J = 0; J <= UB; J++) {
mAArr[J] *= mGeta;
}
long Result = Solve();
sb.Append((decimal)Result / mGeta);
sb.AppendLine();
}
Console.Write(sb.ToString());
}
// セパレータとLong型の列挙を引数として、結合したstringを返す
static string LongEnumJoin(string pSeparater, IEnumerable<long> pEnum)
{
string[] StrArr = Array.ConvertAll(pEnum.ToArray(), pX => pX.ToString());
return string.Join(pSeparater, StrArr);
}
static long mK;
static long mX;
static long[] mAArr;
static long UB;
static long Solve()
{
long L = 0;
long R = mAArr.Max();
while (L + 1 < R) {
long Mid = (L + R) / 2;
if (CanAchieve(Mid)) {
L = Mid;
}
else {
R = Mid;
}
}
return L;
}
static bool CanAchieve(long pShikii)
{
long MoreShikiiCnt = 0;
long RestDivCnt = mK;
foreach (long EachVal in mAArr) {
long CurrVal = EachVal;
if (CurrVal < pShikii) continue;
// 完全二分の深さを求める
int Depth = 1;
long CanDivCnt = (1L << (Depth - 1)) - 1;
while (CurrVal / 2 >= pShikii) {
CurrVal /= 2;
Depth++;
CanDivCnt = (1L << (Depth - 1)) - 1;
if (CanDivCnt > RestDivCnt) break;
}
long DivCnt = Math.Min(CanDivCnt, RestDivCnt);
RestDivCnt -= DivCnt;
MoreShikiiCnt += 1 + DivCnt;
}
if (RestDivCnt > 0) {
MoreShikiiCnt -= RestDivCnt;
}
return MoreShikiiCnt >= mX;
}
}
解説
答えを二分探索し
pShikii以上の棒をX本作れるかの判定メソッドを解くことを考えます。
pShikii以上の棒をX本作れるかの判定メソッドでは、
2本に分割しても損しない棒は、2本に分割する。
分割のツリーは、下記の完全二分木になるので、
分割した回数は、完全二分木の深さから分かります。
●深さが1の場合 0
●深さが2の場合 1
●深さが3の場合 3
●深さが4の場合 7
●深さが5の場合 15
分割したことによって、pShikii以上の棒の数も求まります。
最後に、分割回数が不足する場合に、
pShikii以上の棒が減る分割を行うことを考慮すれば、
判定メソッド作れます。
また、TLE対策として、制約より棒の長さは10^9以下なので
double型の回避で、2^32の下駄を履かせてます。
long.MaxValue - 10^9 * Math.Pow(2, 32) は正なので、
オーバフローしないのと
2**30 > 10^9 + 10^5
なので、整数型の範囲で計算することができます。