AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC200-E Patisserie ABC 2
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 5");
//1 2 2
}
else if (InputPattern == "Input2") {
WillReturn.Add("1000000 1000000000000000000");
//1000000 1000000 1000000
}
else if (InputPattern == "Input3") {
WillReturn.Add("9 47");
//3 1 4
}
else if (InputPattern == "Input4") {
WillReturn.Add("1000000 219466283770414767");
//469174 260221 367435
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long mN;
static long mK;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = InputList[0].Split(' ').Select(pX => long.Parse(pX)).ToArray();
mN = wkArr[0];
mK = wkArr[1];
Console.WriteLine(Solve());
}
static long[] mDPResult2;
static string Solve()
{
long UB = mN * 3;
// 場合の数[和]なDP表
long[] PrevDP = new long[UB + 1];
PrevDP[0] = 1;
for (long I = 1; I <= 3; I++) {
long[] CurrDP = new long[UB + 1];
for (long J = 0; J <= UB; J++) {
if (PrevDP[J] == 0) continue;
// いもす法で更新するIndのMinとMax
long UpdateIndMin = J + 1;
long UpdateIndMax = J + mN + 1;
CurrDP[UpdateIndMin] += PrevDP[J];
if (UpdateIndMax <= UB) {
CurrDP[UpdateIndMax] -= PrevDP[J];
}
}
// 累積和に変更してから、すり替え
for (long J = 1; J <= UB; J++) {
CurrDP[J] += CurrDP[J - 1];
}
PrevDP = CurrDP;
// DP結果を保存しておく
if (I == 2) mDPResult2 = (long[])CurrDP.Clone();
}
long TargetSum = 0;
long PatternCntSum1 = 0;
long TargetRank = mK;
// A+B+Cを決定する
for (long I = 0; I <= UB; I++) {
long PrevSum = PatternCntSum1;
PatternCntSum1 += PrevDP[I];
if (PatternCntSum1 >= TargetRank) {
TargetSum = I;
TargetRank -= PrevSum;
break;
}
}
// Aを決定する
long DecideA = long.MaxValue;
long PatternCntSum2 = 0;
for (long LoopA = 1; LoopA <= TargetSum - 2; LoopA++) {
// 下限のチェック
if (TargetSum > LoopA + mN * 2) continue;
long PrevSum = PatternCntSum2;
PatternCntSum2 += DerivePatternCnt2(TargetSum, LoopA);
if (PatternCntSum2 >= TargetRank) {
DecideA = LoopA;
TargetRank -= PrevSum;
break;
}
}
// Bを決定する
long DecideB = long.MaxValue;
for (long LoopB = 1; LoopB <= TargetSum - 2; LoopB++) {
// 下限のチェック
if (TargetSum > DecideA + LoopB + mN) continue;
if (--TargetRank == 0) {
DecideB = LoopB;
break;
}
}
long DecideC = TargetSum - DecideA - DecideB;
return string.Format("{0} {1} {2}", DecideA, DecideB, DecideC);
}
// 和とAの値を引数にして、BとCの順列が何通りあるかを返す
static long DerivePatternCnt2(long pSum, long pA)
{
long RestSum = pSum - pA;
return mDPResult2[RestSum];
}
}
解説
最初にDPで、
2個取った時の、場合の数[和]と
3個取った時の、場合の数[和]を求めておきます。
次に、
OrderBy A+B+C ASC , A ASC , B ASC
でソートした時のK番目が欲しいので
A+B+Cの昇順で件数の累積和を求め、累積和がK以上になったら、
その時のA+B+Cに注目します。
Kからは、1つ前の累積和を引いておきます。
次にAの昇順で件数の累積和を求め、累積和がK以上になったら、
その時のAに注目します。
Kからは、1つ前の累積和を引いておきます。
次にBとCを、線形オーダーでいいので、求めれば、解が分かります。