AtCoderのARC
次のARCの問題へ
前のARCの問題へ
ARC159-B GCD Subtraction
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("15 9");
//2
}
else if (InputPattern == "Input2") {
WillReturn.Add("1 1");
//1
}
else if (InputPattern == "Input3") {
WillReturn.Add("12345678910 10987654321");
//36135
}
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[] wkArr = GetSplitArr(InputList[0]);
long A = wkArr[0];
long B = wkArr[1];
long Result = Solve(A, B);
Console.WriteLine(Result);
}
static long Solve(long pA, long pB)
{
long A = Math.Max(pA, pB);
long B = Math.Min(pA, pB);
if (A == B) {
return 1;
}
long[] YakusuuArr = DeriveYakusuuArr(A - B);
long Answer = 0;
while (true) {
if (B == 0) {
break;
}
if (A == B) {
Answer++;
break;
}
long CurrGCD = DeriveGCD2(A, B);
// 残り操作回数[GCD]なDict
var RestCntDict = new SortedDictionary<long, long>();
RestCntDict[CurrGCD] = B / CurrGCD;
foreach (long EachYakusuu in YakusuuArr) {
if (EachYakusuu <= CurrGCD) continue;
if (CurrGCD > 1) {
if (EachYakusuu % CurrGCD > 0) continue;
}
RestCntDict[EachYakusuu] = (A % EachYakusuu) / CurrGCD;
}
var TargetItem = RestCntDict.OrderBy(pX => pX.Value).First();
A -= CurrGCD * TargetItem.Value;
B -= CurrGCD * TargetItem.Value;
Answer += TargetItem.Value;
}
return Answer;
}
// ユークリッドの互除法で2数の最大公約数を求める
static long DeriveGCD2(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;
}
}
// 約数を列挙
static long[] DeriveYakusuuArr(long pTarget)
{
var YakusuuSet = new HashSet<long>();
for (long I = 1; I * I <= pTarget; I++) {
if (pTarget % I == 0) {
YakusuuSet.Add(I);
YakusuuSet.Add(pTarget / I);
}
}
long[] YakusuuArr = YakusuuSet.ToArray();
Array.Sort(YakusuuArr);
return YakusuuArr;
}
}
解説
A >= B
としても解は変わらないので、
AとBの大小関係を固定して考えます。
具体例として、
118と78で考えます
A B GCD
118 78 2
116 76 4
112 72 8
104 64 8
96 56 8
88 48 8
80 40 40
40 0
まず、A-Bは不変量だと分かります。
AとBは減算により減りますが、
A-Bは不変であり、GCDは、下記のように図形で考えると
敷き詰め可能な最大の正方形の1辺ですので、
---------------
| | |
| | |
| | |
| | |
---------------
A-B の 約数のみが、
この問題の操作でのGCDになりうると分かります。
また、2の倍数から、2を引いても、2の倍数ですので、
この問題の操作での、次のGCDは、前のGCDの倍数という関係になります。
以上により、
●倍数関係の次のGCDにならずに操作が終了する場合の、Bが0になるまでの操作回数
●倍数関係の次のGCDになる場合の、操作回数
を求めて、最小の操作回数まで操作をスキップし、
TLEにならずに解を求めることができます。