はまやんはまやんはまやん

hamayanhamayan's blog

オンリー・ワン [yukicoder No.546]

https://yukicoder.me/problems/no/546

前提知識

解法

https://yukicoder.me/submissions/189148

C[0]でだけ割り切れる数、C[1]でだけ割り切れる数をそれぞれ別々に計算して足し合わせることにする。
xでだけ割り切れる数と言うのは、xで割り切れる数を全体集合とすると、「n(U) - n(x以外のCの和集合)」と考えることができる。
前半部分は簡単に計算ができるが、後半部分は工夫が必要。
和集合の個数を数えるには包除原理を使うというセオリーがあり、実際、使えそうなので後半は包除原理で求める。
普通の包除原理をすればいいのだが、xで割り切れる数が全体集合であるため、lcmを取るときにxを必ず含める。

ライブラリ群
countMultiple(ll L, ll R, ll div)は[L,R]の範囲でdivで割り切れる個数を返す関数
下のlcmは少し複雑に書いてあるが、上限付きのlcmでlcmがlong longを越える場合があるため、INFを超えそうならINFにまとめるようにしてある。

typedef long long ll;
ll countMultiple(ll L, ll R, ll div) {
    if (R % div == 0) {
        if (L % div == 0) return R / div - L / div + 1;
        else return R / div - L / div;
    } else {
        if (L % div == 0) return R / div - L / div + 1;
        else return R / div - L / div;
    }
}
typedef long long ll; typedef long double ld;
#define INF 1LL<<60
ld lginf = -1;
ll mul(ll a, ll b) {
    if (lginf < 0) lginf = log10l(INF); ld aa = log10l(a), bb = log10l(b);
    if (lginf <= aa + bb) return INF; return a * b;
}
ll gcd(ll a, ll b) { return a ? gcd(b%a, a) : b; }
ll lcm(ll a, ll b) { if (a == INF || b == INF) return INF; return mul(a / gcd(a, b), b); }
int N;
ll L, H;
ll C[11];
ll cn[11];
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> N >> L >> H;
    rep(i, 0, N) cin >> C[i];

    ll ans = 0;
    rep(i, 0, N) {
        vector<ll> CC;
        ll d = 0;
        rep(j, 0, N) if (i != j) CC.push_back(C[j]);
        rep(mask, 0, 1<<(N - 1)) {
            ll lc = C[i];
            int cnt = 0;
            rep(j, 0, N - 1) if (mask & (1 << j)) lc = lcm(lc, CC[j]), cnt++;

            ll c = countMultiple(L, H, lc);
            if (cnt % 2 == 0) d += c;
            else d -= c;
        }
        ans += d;
    }
    cout << ans << endl;
}