AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC216-E Amusement Park
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("3 5");
WillReturn.Add("100 50 102");
//502
}
else if (InputPattern == "Input2") {
WillReturn.Add("2 2021");
WillReturn.Add("2 3");
//9
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long mK;
static long[] mAArr;
static long UB;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = InputList[0].Split(' ').Select(pX => long.Parse(pX)).ToArray();
mK = wkArr[1];
mAArr = InputList[1].Split(' ').Select(pX => long.Parse(pX)).ToArray();
UB = mAArr.GetUpperBound(0);
// 昇順にソートしておく
Array.Sort(mAArr);
UB = mAArr.GetUpperBound(0);
long Shikii = ExecNibunhou(mAArr);
//Console.WriteLine("閾値={0}", Shikii);
long Answer = 0;
long Rest = mK;
for (long I = UB; 0 <= I; I--) {
if (mAArr[I] < Shikii) break;
// 等差数列の和の公式
long Kousuu = mAArr[I] - Shikii + 1;
Answer += (Shikii + mAArr[I]) * Kousuu / 2;
Rest -= Kousuu;
}
//Console.WriteLine("閾値以上のSum={0}", Answer);
// 残りの取得分
if (Shikii > 0) {
Answer += Rest * (Shikii - 1);
}
Console.WriteLine(Answer);
}
// 二分法で、閾値以上のマスを全て埋めれる、最小の閾値を返す
static long ExecNibunhou(long[] pArr)
{
// 閾値1を 達成可能な特殊ケース
if (DeriveOverMasuCnt(1) <= mK) {
return 1;
}
long L = 1;
long R = pArr.Last() + 1;
while (L + 1 < R) {
long Mid = (L + R) / 2;
if (DeriveOverMasuCnt(Mid) <= mK) {
R = Mid;
}
else {
L = Mid;
}
}
return R;
}
// 引数の閾値以上のマスの数を返す
static long DeriveOverMasuCnt(long pVal)
{
long WillReturn = 0;
for (long I = UB; 0 <= I; I--) {
if (mAArr[I] < pVal) break;
WillReturn += mAArr[I] - pVal + 1;
}
return WillReturn;
}
}
解説
昇順にソートしても問題ないので
昇順にソートして棒グラフで考えます。
□□
□□
□□□
□□□□
□□□□□
閾値を決めて、閾値以上のマスがK個以下かは、
単調性があるので、二分探索で最小の閾値を求めます。
閾値が決まったら、閾値以上の合計は、
等差数列の和の公式で求まって、
残りの取得分の和も計算で求まります。