AtCoderのPAST
次のPASTの問題へ
前のPASTの問題へ
第12回PAST I 毎日のリンゴ
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("10");
WillReturn.Add("4 5 8");
WillReturn.Add("8 9 10");
WillReturn.Add("4 10 7");
WillReturn.Add("10 1 5");
WillReturn.Add("6 4 9");
WillReturn.Add("304607 77 89");
WillReturn.Add("291969 231 9");
WillReturn.Add("424790 216 294");
WillReturn.Add("76881 213 111");
WillReturn.Add("312895 58 1");
//14
//36
//12
//20
//24
//13402703
//875907
//61169622
//4151490
//0
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long mN;
static long mA;
static long mM;
static long mLCM;
static void Main()
{
List<string> InputList = GetInputList();
var sb = new System.Text.StringBuilder();
foreach (string EachStr in InputList.Skip(1)) {
long[] wkArr = EachStr.Split(' ').Select(pX => long.Parse(pX)).ToArray();
mN = wkArr[0];
mA = wkArr[1];
mM = wkArr[2];
mLCM = DeriveLCM2(mA, mM);
long Result = Solve();
sb.Append(Result);
sb.AppendLine();
}
Console.Write(sb.ToString());
}
static long Solve()
{
var AppearList = new List<long>();
var AppearSet = new HashSet<long>();
long[] PreCycleArr;
long[] CycleArr;
long CurrDay = 1;
long CurrHash = GetHash(CurrDay);
while (true) {
if (AppearSet.Contains(CurrHash)) {
PreCycleArr = AppearList.TakeWhile(pX => pX != CurrHash).ToArray();
CycleArr = AppearList.SkipWhile(pX => pX != CurrHash).ToArray();
break;
}
AppearList.Add(CurrHash);
AppearSet.Add(CurrHash);
CurrDay++;
CurrHash = GetHash(CurrDay);
}
// 周期に入る前の分を加算
long RestN = mN;
long TakeCnt = Math.Min(RestN, PreCycleArr.Length);
long Answer = GetSum(PreCycleArr.Take((int)TakeCnt));
RestN -= PreCycleArr.Length;
if (RestN == 0) {
return Answer;
}
// 周期の分を加算
if (CycleArr.Length <= RestN) {
long OneCycleSum = GetSum(CycleArr);
Answer += OneCycleSum * (RestN / CycleArr.Length);
RestN %= CycleArr.Length;
}
// 残りの分を加算
Answer += GetSum(CycleArr.Take((int)RestN));
return Answer;
}
// ハッシュ値の列挙を引数として、「悲しさ」の総和を返す
static long GetSum(IEnumerable<long> pEnum)
{
long WillReturn = 0;
foreach (long EachHash in pEnum) {
long Val1 = EachHash / Geta;
long Val2 = EachHash % Geta;
if (Val1 == 0) Val1 = mLCM;
if (Val2 == 0) Val2 = mLCM;
WillReturn += Math.Abs(Val2 - Val1);
}
return WillReturn;
}
const long Geta = 1000000000;
// 日を引数として、リンゴ価格と、クーポン総額のハッシュ値を返す
static long GetHash(long pDay)
{
// リンゴ価格
long ApplePrice = mA * pDay;
long Syou = ApplePrice / mM;
if (ApplePrice % mM > 0) Syou++;
long CouponPrice = Syou * mM;
// ハッシュ値はmod LCMで考える
long Val1 = ApplePrice % mLCM;
long Val2 = CouponPrice % mLCM;
return Val1 * Geta + Val2;
}
// 2つの数のLCMを求める
static long DeriveLCM2(long p1, long p2)
{
long GCD = DeriveGCD(p1, p2);
return (p1 / GCD) * p2;
}
// ユークリッドの互除法で2数の最大公約数を求める
static long DeriveGCD(long pVal1, long pVal2)
{
long WarareruKazu = pVal2;
long WaruKazu = pVal1;
while (true) {
long Amari = WarareruKazu % WaruKazu;
if (Amari == 0) return WaruKazu;
WarareruKazu = WaruKazu;
WaruKazu = Amari;
}
}
}
解説
リンゴが5円ずつで、クーポンが8円ずつで考察します。
5 8
10 16
15 16
20 24
25 32
30 32
35 40
40 40
45 48
50 56
5と8のLCMでmodを取れば周期性があると分かるので、
周期性を使って解いてます。