[HAOI2015] 按位或
前置知识
最好做过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 \bigcap S \neq \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\n", ans);
return 0;
}