[HAOI2015] 按位或 题解

min_max 容斥

Statement

一个数字,从 \(0\) 开始,每次按位或上一个数 \(𝑥 ∈ [0,2^𝑛 − 1]\) ,其中 \(𝑖\) 出现的概率是 \(𝑝_𝑖\)

求期望多少次后该数变为 \(2^𝑛 − 1\)

\(𝑛 ≤ 20,0 ≤ 𝑝_𝑖 ≤ 1,\sum𝑝_𝑖 = 1\)

Solution

前置题目

它的一道前置题目叫做: HDU4336 Card Collector

每包零食里有一张卡牌,总共有 \(N\) 种不同的卡牌,得到这 \(N\) 种卡牌的概率分别为 $ P_i(1 <= i <= N)$

求收集到所有卡牌的期望是多少。

我们不妨先看看这道题怎么做

这里涉及到一个前置知识叫做 几何分布

几何分布

\(P (𝑋)\) 表示进行成功率为 \(𝑝\) 的独立实验,前 \(𝑋 − 1\) 次失败,第 \(𝑋\) 次成功的概率

那么有 \(P (𝑋) = 𝑝 (1 − 𝑝)^{ 𝑋−1}\) ,\(E (𝑋) = \frac1 𝑝\)

我们知道了如果至少抽到 \(i\) 这张卡牌的期望是 \(\frac 1{p_i}\)

也就是说 \(min\) 很好求而 \(max\) 不好求,所以我们考虑 \(min-max\) 容斥,定义 \(s\) 为卡片集合

那么 \(𝐸(\max(𝑠))\)\(𝑠\) 中所有卡片都收集到的期望, \(𝐸(\min(𝑠))\)\(𝑠\) 中至少收集到 \(1\) 张卡片的期望

容易知道: \(E(\min(s))=\dfrac 1{\sum _{i\in s}p_i}\) ,套用公式:

\[E(\max(s))=\sum_{T\subseteq s}(-1)^{|T|-1}E(\min(s)) \]

解决了这个问题。

#include<bits/stdc++.h>
#define eps 1e-8
using namespace std;
const int N = 25;

double a[N],ans;
int n;

void dfs(int x,int cnt,double sum){
    if(x==n+1){
        if(sum<eps)return ;
        if(cnt&1)ans+=1/sum;
        else ans-=1/sum;
        return 0;
    }
    dfs(x+1,cnt,sum);
    dfs(x+1,cnt+1,sum+a[x]);
}

signed main(){
    while(cin>>n){
        for(int i=1;i<=n;++i)cin>>a[i];
        ans=0,dfs(1,0,0);
        printf("%.8lf\n",ans);
    }
    return 0;
}

原问题

现在我们看到原问题,自然而然开始考虑同样的 \(min-max\) 容斥,考虑 \(E(\min(s))\) 的求法

\(f(s)\) 表示选中 \(s\) 某一个子集的概率,有:\(P(min(s)=k)=f(\complement_Us)^{k-1}(1-f(\complement_Us))\)

\(\min(s) = 𝑘\) 意味着前 \(𝑘 − 1\) 次全部选到了 \(𝑆\) 的补集 \(∁𝑆\)当中,第 \(𝑘\) 次选到了 \(s\) 当中的元素

之所以是 \(1 − 𝑓 (∁𝑆)\) 而不是 \(𝑓(𝑆)\) 是因为并不一定要全部选到 \(𝑆\) 当中,一部分在 \(𝑆\) 内而一部分在 \(𝑆\) 外仍是合法情况,即只 要不全在 \(∁𝑆\) 中即可

容易发现 \(E(\min(s))=\dfrac 1{1-f(\complement s)}\)

考虑 \(f(\complement s)\) 的求法,容易发现 \(f(\complement s)=\sum_{T\subseteq s}p_t\) ,显然一个 FWT 就好。

Code

#include<bits/stdc++.h>
#define eps 1e-7
using namespace std;
const int N = 1<<23;

double a[N],ans;
int n;

signed main(){
    scanf("%d",&n);
    for(int i=0;i<(1<<n);++i)scanf("%lf",&a[i]);
    for(int i=2;i<=(1<<n);i<<=1)
        for(int j=0,p=i>>1;j<=(1<<n);j+=i)
            for(int k=j;k<j+p;++k)a[k+p]+=a[k];
    // for(int i=0;i<(1<<n);++i)cout<<a[i]<<endl;
    for(int i=1;i<(1<<n);++i){
        if(1-a[((1<<n)-1)^i]<eps)return puts("INF"),0;
        double ad=1/(1-a[((1<<n)-1)^i]);
        if(__builtin_popcount(i)%2)ans+=ad;
        else ans-=ad;
    }
    printf("%.10lf",ans);
    return 0;
}
posted @ 2022-01-11 21:15  _Famiglistimo  阅读(19)  评论(0编辑  收藏  举报