srupのメモ帳

競プロで解いた問題や勉強したことを記録していくメモ帳

yukicoder No.316 もっと刺激的なFizzBuzzをください

問題

問題概要

a,b,cのどれかの倍数になる数字の個数を求める。

解法

包徐原理で解く。高1のどっかでやる3つの場合の公式を使えば解くことができる。 wikipediaの3つの有限集合に対して書かれている公式をそのまま使えばいい。

包除原理 - Wikipedia

集合a,b,cの数を求めるには、単に、nをa,b,cで割ればいい。このときに、1からnまでaで割れる数がいくつかるかをループでやろうとするとnが大きいので、TLEしてしまうので注意。次に、2つ含まれた集合a∧b,b∧c,c∧aについてである。集合a∧bについて考える。つまり、aの倍数でもあり、bの倍数でもある数の集合である。これをどのように考えるかであるが、 aの倍数でもあり、bの倍数でもある最小の数は、aとbのlcm。よって、lcmの倍数がnまで含まれる個数が、a∧bの集合の個数なる。同様にほかの2個の場合もできる。あとは、集合a∧b∧cの集合の数。これも2つの時と同じように考えて、a,b,cのlcmを求めて計算すればいい。あとは求まった数を包徐原理の公式に突っ込めばいい。

ミス

fizzbizzという文字列をTLで見たので解いた。 ∧(かつ)の意味で書いた。

コード

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int i=0;i<(n);i++)

ll lcm(ll d1, ll d2){
    return d1 / __gcd(d1, d2) * d2;
}

int main(void){
    ll n, a, b, c;
    cin >> n >> a >> b >> c;
    ll ua = n / a;
    ll ub = n / b;
    ll uc = n / c;
    ll uab = n / lcm(a, b);
    ll ubc = n / lcm(b, c);
    ll uca = n / lcm(c, a);
    ll uabc = n / lcm(a, lcm(b, c));
    printf("%lld\n", ua + ub + uc - uab - ubc - uca + uabc);
    return 0;
}