AOJ 2446 Enumeration + 高速メビウス変換まとめ
制約条件
n≦20
m≦10^18
方針
包除原理。
高速メビウス変換というものを使うと、集合に対する関数f, gがあって、
f(S) = Σ[T⊆S](-1)^|S - T| g(T)
が成り立っていて、g(S)がわかっているとき、全てのSに対してf(S)を、
O(n2^n)で求められる。
この問題にこの式を適用するとき、Sは{i | 1≦i≦n}の部分集合で、
g(S) = | ∩[i∈S] Ai | とすれば、
f(S) = -(-1)^|S| | ∪[i∈S] Ai |となる。
なので、絶対値をとれば、| ∪[i∈S] Ai |が簡単に求まる。
高速ゼータ変換は結構情報があるのだけれど、その逆変換であるこれはあまり情報がなかった。
高速ゼータ変換については
- http://topcoder.g.hatena.ne.jp/iwiwi/20120422/1335065228
- http://d.hatena.ne.jp/todo314/20120614/1339695202
のあたりを参照に。
rep(i, n) rep(j, 1 << n) if(!(j & 1 << i)) f[j] += f[j | 1 << i];
でできる。
で、高速メビウス変換は、
rep(i, n) rep(j, 1 << n) if(j & 1 << i) f[j] -= f[j ^ 1 << i];
でできて。
証明は以下の通り。
f0(S) = g(S)
fk(S) =
k∈Sでない fk-1(S)
k∈Sである fk-1(S-{K}) + fk-1(S)
とすると、
fn(S) = f(S)であることを示せばよい。
fk(S) = Σ[T⊆S, S-T⊆{1, 2, ..., k}] (-1)^|S-T| g(T)であることを示せばよい。
k = 0のときは定義から成り立つ。
k∈Sでないとき、S-T⊆{1, 2, ..., k}は{1, 2, ..., k-1}を満たすから、fk(S) = fk-1(S)
k∈Sのとき、総和の部分はS-Tがkを含むか含まないかの二つに分けられて、
Σ[T⊆S, S-T⊆{1, 2, ..., k-1}] (-1)^|S-T| g(T)
Σ[T⊆S, k∈S-T, S-T⊆{1, 2, ..., k}] (-1)^|S-T| g(T)
前者はfk-1(S)そのもの。
後者は、Sにkが含まれ、Tにkが含まれないので、
Σ[T⊆S-{K}, S-{k}-T⊆{1, 2, ..., k-1}] -(-1)^|S-{k}-T| g(T)とかけて、
これは-fk-1(S-{K})に相当する。
すなわちfk = -fk-1(S-{K}) + fk-1(S)
ソースコード
ll n, m, a[20], p[20], f[1 << 20]; int main(){ cin >> n >> m; rep(i, n) cin >> a[i]; rep(i, n) cin >> p[i]; rep(i, 1 << n){ ll t = 1; rep(j, n) if(i & 1 << j){ ll u = a[j] / __gcd(t, a[j]); if(1.0 * t * u < 1.5e18) t *= u; else t = m + 1; } f[i] = m / t; } rep(i, n) rep(j, 1 << n) if(j & 1 << i) f[j] -= f[j ^ 1 << i]; long double ans = 0; rep(i, 1 << n){ long double e = 1; rep(j, n) e *= (i & 1 << j ? p[j] : 100 - p[j]) / 100.0l; ans += e * (m - abs(f[i])); } cout << fixed << setprecision(20) << ans << endl; return 0; }