yokobuttonの不定期で競技プログラミングをするブログ

不定期で解けた競技プログラミングコンテストの問題を載せています。

NOMURA プログラミングコンテスト2022(AtCoder Beginner Contest 253)D - FizzBuzz Sum Hard

問題の要約
1以上N以下の整数であって、Aの倍数でもBの倍数でもないものの総和を求めよ。


制約
1<=N,A,B<=10^9


入力
N A B


考え方
1,1からNの総和を求める。これはN*(N+1)/2で求めることができる。
2,上の数からAの倍数の総和を引く。Aの倍数の総和はN/A*(N/A+1)/2*Aで求めることができる。
3,上の数からBの倍数の総和を引く。Bの倍数の総和はN/B*(N/B+1)/2*Bで求めることができる。
4,上の数からAの倍数かつBの倍数の倍数(AとBの最小公倍数の倍数)の総和を足す。AとBの最小公倍数lcm_numの総和はN/lcm_num*(N/lcm_num+1)/2*lcm_numで求めることができる。
5,答えを出力する。


(注)倍数の総和は簡単な例で導出可能である。
  例えば、3の倍数は3,6,9,12,...となるが、これは3をくくりだせば、3*(1,2,3,4,...)となるので、1からNまでの総和がわかっていれば、ある程度簡単に導出できる。
  あとはNまで何個の倍数があればよいか分かればよい。
  例えば、3の倍数で10までだと3,6,9の3つだけだが、これはfloor(10/3)で求めることができる。

 

実際のプログラム
#include<iostream>
#include<numeric>

using namespace std;

int main(){
  long long N,A,B;
  cin >> N >> A >> B;
  
  if(A > B){
    swap(A,B);
  }
  
  long long sum = N*(N+1)/2;
  sum -= N/A*(N/A+1)/2*A;
  sum -= N/B*(N/B+1)/2*B;
  
  long long lcm_num = lcm(A,B);
  sum += N/lcm_num*(N/lcm_num+1)/2*lcm_num;
  
  cout << sum << endl;
  
  return 0;
}