AtCoderのPAST
次のPASTの問題へ
前のPASTの問題へ
第8回PAST G K番目の要素
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 4");
WillReturn.Add("3 2 2");
WillReturn.Add("2 3 4");
//6
}
else if (InputPattern == "Input2") {
WillReturn.Add("2 10");
WillReturn.Add("4 1000000000 1000000000");
WillReturn.Add("6 1000000000 1000000000");
//6000000000
}
else if (InputPattern == "Input3") {
WillReturn.Add("5 10");
WillReturn.Add("3 1 4");
WillReturn.Add("1 5 9");
WillReturn.Add("2 6 5");
WillReturn.Add("3 5 8");
WillReturn.Add("9 7 9");
//16
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
struct SuuretuInfoDef
{
internal long Kousuu;
internal long Syokou;
internal long Kousa;
internal long Makkou;
}
static List<SuuretuInfoDef> mSuuretuInfoList = new List<SuuretuInfoDef>();
static long mK;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = { };
Action<string> SplitAct = pStr =>
wkArr = pStr.Split(' ').Select(pX => long.Parse(pX)).ToArray();
SplitAct(InputList[0]);
mK = wkArr[1];
foreach (string EachStr in InputList.Skip(1)) {
SplitAct(EachStr);
SuuretuInfoDef WillAdd;
WillAdd.Kousuu = wkArr[0];
WillAdd.Syokou = wkArr[1];
WillAdd.Kousa = wkArr[2];
WillAdd.Makkou = WillAdd.Syokou + WillAdd.Kousa * (WillAdd.Kousuu - 1);
mSuuretuInfoList.Add(WillAdd);
}
long L = 0;
long R = mSuuretuInfoList.Max(pX => pX.Makkou);
while (L + 1 < R) {
long Mid = (L + R) / 2;
if (IsUpperOrEqual(Mid)) {
R = Mid;
}
else {
L = Mid;
}
}
Console.WriteLine(R);
}
// 値を引数として、順位がK位、または、K位より悪いか判定
static bool IsUpperOrEqual(long pVal)
{
// 引数と同じか、良い順位の値の件数
long LowerOrEqualCntSum = 0;
foreach (SuuretuInfoDef EachSuuretuInfo in mSuuretuInfoList) {
long CurrLowerOrEqualCnt = DeriveLowerOrEqualCnt(pVal, EachSuuretuInfo);
LowerOrEqualCntSum += CurrLowerOrEqualCnt;
if (LowerOrEqualCntSum >= mK) return true;
}
return LowerOrEqualCntSum >= mK;
}
// 二分法で、等差数列のpVal以下の項数を求める
static long DeriveLowerOrEqualCnt(long pVal, SuuretuInfoDef pSuuretuInfo)
{
// 末項がpVal以下の特殊ケース
if (pSuuretuInfo.Makkou <= pVal) {
return pSuuretuInfo.Kousuu;
}
// 初項がpValより大きい特殊ケース
if (pSuuretuInfo.Syokou > pVal) {
return 0;
}
long L = 1;
long R = pSuuretuInfo.Kousuu;
while (L + 1 < R) {
long Mid = (L + R) / 2;
long MidVal = pSuuretuInfo.Syokou + pSuuretuInfo.Kousa * (Mid - 1);
if (MidVal <= pVal) {
L = Mid;
}
else {
R = Mid;
}
}
return L;
}
}
解説
5番目は5位なので、順位で考えることができます。
下記の数列で5位の要素を考えます。
30 30 30 60 60 60 90 90 90
順位が5位または5位より悪い順位であることは、
自分以下の要素数が5以上であることと同値になります。
後は、下記の、値に対する順位の単調性を示す図をふまえて、
二分探索を行うことができ、
5位または5位より悪い順位である、最小の値が解となります。
----------------------------------------------
| 5位より良い順位 | 5位または5位より悪い順位 |
----------------------------------------------
値 ------------------------------------------->
また、交差がプラスの等差数列で、閾値以下の値が何項あるかは、
二分探索で求めることができます。
類題