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

ABC206-E Divide Both


問題へのリンク


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("3 7");
            //2
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("4 10");
            //12
        }
        else if (InputPattern == "Input3") {
            WillReturn.Add("1 1000000");
            //392047955148
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    static void Main()
    {
        List<string> InputList = GetInputList();
        long[] wkArr = InputList[0].Split(' ').Select(pX => long.Parse(pX)).ToArray();
        long L = wkArr[0];
        long R = wkArr[1];

        CreateSoinsuuListDict(R);

        long Answer = 0;
        for (long I = L; I <= R; I++) {
            if (I == 1) continue;

            if (mSoinsuuListDict.ContainsKey(I) == false) continue;

            // 素因数の配列
            long[] SoinsuuArr = mSoinsuuListDict[I].ToArray();

            List<JyoutaiDef> DFSResult = ExecDFS(SoinsuuArr);

            foreach (JyoutaiDef EachJyoutai in DFSResult) {
                long CurrCnt = DeriveBaisuuCnt(I + 1, R, EachJyoutai.ProdVal);

                // 包除原理で、複数の「または」の分を計算する
                if (EachJyoutai.SelectedCnt % 2 == 1) {
                    Answer += CurrCnt;
                }
                else {
                    Answer -= CurrCnt;
                }
            }
            Answer -= DeriveBaisuuCnt(I + 1, R, I);
        }
        Console.WriteLine(Answer * 2);
    }

    // エラトステネスの篩を応用して、素因数のListを設定
    static Dictionary<long, List<long>> mSoinsuuListDict = new Dictionary<long, List<long>>();

    static void CreateSoinsuuListDict(long pJyougen)
    {
        for (int I = 2; I <= pJyougen; I++) {
            if (mSoinsuuListDict.ContainsKey(I)) continue;
            for (int J = I * 2; J <= pJyougen; J += I) {
                if (mSoinsuuListDict.ContainsKey(J) == false) {
                    mSoinsuuListDict[J] = new List<long>();
                }
                mSoinsuuListDict[J].Add(I);
            }
        }
    }

    // 範囲の開始と終了と、数Aを指定して、Aの倍数が何個あるかを返す
    static long DeriveBaisuuCnt(long pSta, long pEnd, long pA)
    {
        long StaSyou = pSta / pA;
        if (pSta % pA > 0) {
            StaSyou++;
        }
        long EndSyou = pEnd / pA;
        return EndSyou - StaSyou + 1;
    }

    struct JyoutaiDef
    {
        internal long CurrInd;
        internal long SelectedCnt;
        internal long ProdVal;
    }

    // 配列から1個以上の要素を選択した時の、積と選択数のListを返す
    static List<JyoutaiDef> ExecDFS(long[] pArr)
    {
        var WillReturn = new List<JyoutaiDef>();

        var Stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        WillPush.CurrInd = 0;
        WillPush.SelectedCnt = 0;
        WillPush.ProdVal = 1;
        Stk.Push(WillPush);

        while (Stk.Count > 0) {
            JyoutaiDef Popped = Stk.Pop();

            if (Popped.SelectedCnt > 0) {
                WillReturn.Add(Popped);
            }

            for (long I = Popped.CurrInd; I <= pArr.GetUpperBound(0); I++) {
                WillPush.CurrInd = I + 1;
                WillPush.SelectedCnt = Popped.SelectedCnt + 1;
                WillPush.ProdVal = Popped.ProdVal * pArr[I];
                Stk.Push(WillPush);
            }
        }
        return WillReturn;
    }
}


解説

4 5 6 7 8 9 10
で X < Y な組み合わせを考えると

(4, 6)
(4,10)
(6. 8)
(6. 9)
(6.10)
(8.10)
が解です。

6を組み合わせの小さいほうにして考えると、
(6+1)からRまでに
6の素因数の倍数であれば、解になると分かります。
6の素因数は2と3なので
その個数は、
 (6+1)からRまでの2の倍数の個数
+(6+1)からRまでの3の倍数の個数
-(6+1)からRまでの6の倍数の個数
と包除原理で求めることができます。

さらに、(6+1)からRまでの6の倍数は、GCDが6になってしまい、
解にならないので、この個数は引く必要があります。

最初に、X < Y で考えたので、
最後には、個数は2倍する必要があります。