AOJ 2446 Enumeration
解法
簡単のために,試しに2つの数字 a, b を用意して考えてみます.a, b が選ばれる確率をそれぞれ p, q とします.
まず,a のみを選んだ場合の期待値は,
- (m / a) * p * (1 - q)
となります.
b のみを選んだ場合は,
- (m / b) * (1 - p) * q
となります.
最後に a と b の両方を選んだ場合は,
- (m / a + m / b - m / lcm(a, b)) * p * q
となります.
これらを足し合わせてみると,以下のようなきれいな式になります.
- (m / a) * p + (m / b) * q - (m / lcm(a, b)) * p * q
これは一般の場合でも成り立つので,結局包除原理で簡単に求めることが出来ます. 2^n 通り試してやればよいです.
lcm のオーバーフローに注意.
ソースコード
#include <bits/stdc++.h> using namespace std; using ll = long long; constexpr ll INF = 2e18; constexpr ll invalid = -1; ll lcm(ll a, ll b) { ll g = __gcd(a, b); a /= g; if(a > INF / b) { return invalid; } return a * b; } int main() { int n; ll m; cin >> n >> m; vector<ll> a(n); vector<double> p(n); for(int i = 0; i < n; ++i) { cin >> a[i]; } for(int i = 0; i < n; ++i) { cin >> p[i]; p[i] /= 100; } double res = 0; for(int S = 1; S < 1 << n; ++S) { int cnt = 0; double q = 1; for(int i = 0; i < n; ++i) { if((S >> i) & 1) { cnt++; q *= p[i]; } } ll t = 1; for(int i = 0; i < n; ++i) { if((S >> i) & 1) { t = lcm(t, a[i]); } if(t == invalid) { break; } } if(t != invalid) { if(cnt & 1) { res += q * (m / t); } else { res -= q * (m / t); } } } cout << fixed << setprecision(10) << res << endl; }