AtCoderのABC    次のABCの問題へ    前のABCの問題へ

ABC444-C AtCoder Riko


問題へのリンク


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("4");
            WillReturn.Add("10 5 5 10");
            //10 15
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("3");
            WillReturn.Add("4 4 4");
            //4
        }
        else if (InputPattern == "Input3") {
            WillReturn.Add("6");
            WillReturn.Add("10 187 344 100 434 257");
            //444
        }
        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[] AArr = GetSplitArr(InputList[1]);
        long UB = AArr.GetUpperBound(0);

        Array.Sort(AArr);
        long SumVal = AArr.Sum();
        long MaxA = AArr.Max();

        long[] YakusuuArr = DeriveYakusuuArr(SumVal);

        var AnswerList = new List<long>();
        foreach (long EachYakusuu in YakusuuArr) {
            if (EachYakusuu > MaxA * 2) break;

            long Ind1 = 0;
            long Ind2 = UB;

            bool IsOK = true;

            long RestCnt = AArr.Length;
            while (RestCnt > 0) {
                long LastVal = AArr[Ind2];
                if (LastVal == EachYakusuu) {
                    Ind2--;
                    RestCnt--;
                    continue;
                }

                long CurrVal = AArr[Ind1];
                long PairVal = AArr[Ind2];
                if (CurrVal + PairVal == EachYakusuu) {
                    Ind1++;
                    Ind2--;
                    RestCnt -= 2;
                    continue;
                }
                IsOK = false;
                break;
            }
            if (IsOK) {
                AnswerList.Add(EachYakusuu);
            }
        }
        Console.WriteLine(LongEnumJoin(" ", AnswerList));
    }

    // セパレータとLong型の列挙を引数として、結合したstringを返す
    static string LongEnumJoin(string pSeparater, IEnumerable<long> pEnum)
    {
        string[] StrArr = Array.ConvertAll(pEnum.ToArray(), pX => pX.ToString());
        return string.Join(pSeparater, StrArr);
    }

    // 約数を列挙する
    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;
    }
}


解説

配列の総和は不変量なので、総和の約数が解候補です。
解候補ごとに、ソート済配列を両端キューとして分配をシュミレーションしてます。

計算量は、総和の最大が3*100000*1000000000 で 10の15乗
素因数分解の計算量はO(ルートN)なので、
素因数分解は間に合う。

また、10の18乗以下の最大の高度合成数の、約数の個数は10万ですが、
両端キューとして分配をシュミレーションでの
枝切りのタイミングは最初にあるので、
間に合います。