前置知识
最好做过hdu4336。
正解
先写一下 min-max 容斥的公式吧,毕竟是我第一次学这个算法。
[max{(S)} = sum_{T in S} (-1)^{|T|-1} min{(T)}
]
(max{(S)}) 表示选出这个集合(的所有元素)的最晚(期望)时间,(min{(T)}) 表示选出这个集合(中的一个元素)的最早(期望)时间。
求出所有的 (min{(T)}) 就能算答案了。
如果 (S) 与 (T) 有交,那么一次就能选中 (T) 的概率可以加上 (p(S))。
现在对于每一个 (T) 要求的就是 :
[sum_{T igcap S
eq varnothing} p(S)
]
考虑其补集,问题就是求个子集和,用高维前缀和预处理一下就完了。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
const double eps = 1e-13;
int n;
double p[1 << N];
bool ok[N];
int main() {
scanf("%d", &n);
for(int sta = 0; sta < (1 << n); ++sta) {
scanf("%lf", &p[sta]);
if(p[sta] < eps) continue;
for(int i = 0; i < n; ++i)
if(sta >> i & 1)
ok[i] = true;
}
for(int i = 0; i < n; ++i)
if(!ok[i]) {
puts("INF");
return 0;
}
for(int i = 0; i < 20; ++i)
for(int sta = 1; sta < (1 << n); ++sta)
if(sta >> i & 1)
p[sta] += p[sta ^ (1 << i)];
double ans = 0;
for(int sta = 1; sta < (1 << n); ++sta) {
double g = 1 - p[((1 << n) - 1) ^ sta];
g = 1 / g;
if(__builtin_popcount(sta) & 1) {
ans += g;
} else {
ans -= g;
}
}
printf("%.7lf
", ans);
return 0;
}