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倍する必要があります。