AtCoderのPAST
次のPASTの問題へ
前のPASTの問題へ
第9回PAST L 嘘つきな生徒たち
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("5 10");
WillReturn.Add("7 5 10 3 2");
//1
}
else if (InputPattern == "Input2") {
WillReturn.Add("5 10");
WillReturn.Add("9 8 7 7 5");
//1
}
else if (InputPattern == "Input3") {
WillReturn.Add("11 1000000000");
WillReturn.Add("0 1 10 100 1000 10000 100000 1000000 10000000 100000000 1000000000");
//10
}
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 void Main()
{
List<string> InputList = GetInputList();
long Result = Solve(InputList);
Console.WriteLine(Result);
}
static long Solve(List<string> pInputList)
{
long[] wkArr = GetSplitArr(pInputList[0]);
long P = wkArr[1];
long[] AArr = GetSplitArr(pInputList[1]);
long UB = AArr.GetUpperBound(0);
// 値の下限以外を設定
long CurrJyougen = P;
var ValueInfoList = new List<ValueInfoDef>();
for (long I = 0; I <= UB; I++) {
var WillAdd = new ValueInfoDef();
WillAdd.CurrVal = AArr[I];
WillAdd.Jyougen = CurrJyougen--;
WillAdd.Distance = AArr[I] + I;
ValueInfoList.Add(WillAdd);
}
// 値の下限を設定
long CurrKagen = 0;
for (long I = UB; 0 <= I; I--) {
ValueInfoList[(int)I].Kagen = CurrKagen++;
}
// 値が範囲内な要素のみ残す
ValueInfoList = ValueInfoList.FindAll(pX => pX.Kagen <= pX.CurrVal && pX.CurrVal <= pX.Jyougen);
// LISを求める用のList
var LisList = ValueInfoList.Select(pX => pX.Distance).ToList();
LisList.Reverse();
long LISLength = ClassDeriveNonStrictLISLength.DeriveLength(LisList);
return AArr.Length - LISLength;
}
class ValueInfoDef
{
internal long CurrVal; // 初期値
internal long Kagen; // 値の下限(狭義単調減少とする必要条件)
internal long Jyougen; // 値の上限(狭義単調減少とする必要条件)
internal long Distance; // 直交座標での原点からのマンハッタン距離
}
}
#region ClassDeriveNonStrictLISLength
// LIS(広義単調増加)の長さを返す
internal class ClassDeriveNonStrictLISLength
{
// 引数 long型の列挙
// 戻り値 LISの長さ
internal static long DeriveLength(IEnumerable<long> pEnum)
{
// LISの最終値の最小値[LISの長さ] なDP表
var DPSortedList = new SortedList<long, long>();
foreach (long EachVal in pEnum) {
if (DPSortedList.Count == 0) {
DPSortedList[1] = EachVal;
continue;
}
long UpsertKeyInd = ExecNibunhou(DPSortedList, EachVal);
long CurrUB = DPSortedList.Count - 1;
var Keys = DPSortedList.Keys;
// 更新する位置によって分岐
if (UpsertKeyInd <= CurrUB) {
DPSortedList[Keys[(int)UpsertKeyInd]] = EachVal;
}
else {
long PrevKey = Keys[(int)CurrUB];
DPSortedList[PrevKey + 1] = EachVal;
}
}
if (DPSortedList.Count == 0) {
return 0;
}
int UB = DPSortedList.Count - 1;
return DPSortedList.Keys[UB];
}
// 二分法で、引数の値を設定する、キーの配列の添字を返す
private static long ExecNibunhou(SortedList<long, long> pDPSortedList, long pTargetVal)
{
long UB = pDPSortedList.Count - 1;
var Keys = pDPSortedList.Keys;
// 最小値未満の場合
if (pTargetVal < pDPSortedList[Keys[0]]) {
return 0;
}
// 最大値以上の場合
if (pTargetVal >= pDPSortedList[Keys[(int)UB]]) {
return UB + 1;
}
long L = 0, R = UB;
while (L + 1 < R) {
long Mid = (L + R) / 2;
if (pTargetVal < pDPSortedList[Keys[(int)Mid]]) {
R = Mid;
}
else {
L = Mid;
}
}
return R;
}
}
#endregion
解説
まず、真だとすると矛盾が発生するものは偽だと分かります。
具体例1 真だとすると、後続で1点ずつ減っていったときに最後の人の得点がマイナスになる場合。
具体例2 真だとすると、前方に1点ずつ増やしていったときに最初の人の得点が上限を超える場合。
次に、LISを考えるのですが、LISに入れなかった値も考える必要があるため、
直交座標での原点(0,0)からの距離を図示してみます。
6789ABCDE
56789ABCD
456789ABC
3456789AB
23456789A
123456789
012345678
たとえば(0,5)の値は5で、6や7にLISの数列として遷移してしまうと、
LISに入れなかった、生徒を含めて狭義単調減少な数列を作れなくなってしまいます。
逆に、(0,5)からは、5以下にLISの数列として遷移すれば、大丈夫だと分かります。
以上により、下記の手順で解くことができます。
手順1 真とすると矛盾する要素をremoveする
手順2 原点からの距離を求め、右から左に向かって、広義単調増加なLISの長さを求める