[HAOI2015] 按位或

前置知识

min-max 容斥

最好做过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;
}
posted @ 2020-06-15 15:29  Lskkkno1  阅读(103)  评论(0编辑  收藏  举报