P3175 [HAOI2015]按位或 FMT Min-Max容斥

题意:

戳这里

分析:

  • 前置芝士:Min-Max容斥,FMT,离散型随机变量的几何分布
  1. Min-Max容斥:

式子:\(\displaystyle \max(S)=\sum_{T\subset S}(-1)^{|T|+1}\min(T)\)

其中 \(\min \max\) 可以互换位置 , 而且最重要的是 Min-Max容斥 在期望形式下仍成立

简单证明:

我们记 S 中最大的数为 \(x\) ,那么只要证明除 \(x\) 以外的数都被消掉了即可

首先 \(min(T)==x\) 当且仅当 \(T={x}\) 的时候才成立,所以 \(x\) 的系数恒为 \(1\)

其次,我们记 \(y\)\(S\) 中排名第 \(K\) 的数,那么 \(min(T)==y\) 的方案数为 \(2^{n-k}\) 中,其中一半的 \(T\) 大小为奇数,一半的 \(T\) 大小为偶数,所以相互抵消之后,\(y\) 的系数为 \(0\)

  1. 离散型随机变量的几何分布

首先定义一个离散型随机变量满足几何分布的形式为,\(P(x==k)=(1-p)^{k-1}p\ (k\in N^+)\) 其中 p 为一个常量参数

然后我们要推一下柿子来求这个服从几何分布的离散型随机变量的期望,我们记离散型随机变量为 \(X\)

\[E(x)=\sum_{i=1}^{\infty} iP(X==i) \\ =\sum_{i=1}^{\infty} i(1-p)^{i-1}p \\ =p\sum_{i=1}^{\infty}i(1-p)^{i-1} \\ =p\sum_{i=0}^{\infty} [(1-p)^i]' \\ =p\ [\ \sum_{i=0}^{\infty}(1-p)^i\ ]' \\ =p\ [\frac{1}{1-(1-p)}]' \\ =p\frac{1}{p^2} \\ =\frac{1}{p} \]

所以我们得到了一个很有用的结论,就是满足几何分布的离散型随机变量的期望等于服从参数的倒数

  1. FMT(快速莫比乌斯变换)

和 FFT 的思想差不多,不过 FFT 是和卷积,而 FMT 是按位或卷积,这里给出 FMT 的定义式:

\(\displaystyle \hat f(x) = \sum_{i\subset x}f(i)\)

和 FFT 一样,FMT 可以分治计算,而且不需要乘上一堆奇奇怪怪的参数 \(a[i+j]=a[i+j],a[i+j+len]=a[i+j+len]\)


我们把每一位看成一个元素,它的值等于它变成一的期望步数

我们要求的的东西可以转化为,\(\displaystyle E(\max(S))=\sum_{T\subset S}(-1)^{|T|+1}E(\min(T))\)

其中 \(\min \max\) 表示达到这个状态所需要最大步数的元素的步数,感性理解一下,当最大步数的元素都变成了 \(1\) 以后,其他元素也都变成了 \(1\)

然后我们 容易发现,\(\min()\) 作为一个离散型随机变量,是满足几何分布的,我们记 \(S\) 的补集为 \(R\) ,一个集合选出一个元素属于它的概率为 \(H\) ,那么柿子如下:

\[H(S)=1-\sum_{T\subset R}H(T) \\ P(\min(S)==k)=(1-H(S))^{k-1}H(S) \\ E(\min(S))=\frac{1}{1-\sum_{T\subset R}H(T)} \]

然后我们整理一下思路,具体步骤就是:

  1. FMT 求出 \(H()\)
  2. Min-Max 容斥求 \(E(\max (S))\)

代码:

代码真短,让我们一起赞美良心出题人吧!

#include<bits/stdc++.h>
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
    const int maxn = (1<<20)+5;
    const double eps = 1e-10;
    int n,lim;
    int cnt[maxn];
    double ans;
    double a[maxn];

    void work()
    {
        scanf("%d",&n);lim=(1<<n);
        for(int i=0;i<lim;i++) scanf("%lf",&a[i]);
        for(int len=1;len<lim;len<<=1)
            for(int i=0;i<lim;i+=(len<<1))
                for(int j=i;j<i+len;j++) a[j+len]+=a[j];
        for(int i=1;i<lim;i++) cnt[i]=cnt[i>>1]+(i&1);
        for(int i=1;i<lim;i++)
        {
            if(1.0-a[(lim-1)^i]<eps) 
            {
                puts("INF");
                return ;
            }
            double tmp=1.0/(1.0-a[(lim-1)^i]);
            if(cnt[i]&1) ans+=tmp;
            else ans-=tmp;
        }
        printf("%.7f\n",ans);
    }


}

int main()
{
    zzc::work();
    return 0;
}
posted @ 2021-01-28 14:23  youth518  阅读(94)  评论(0编辑  收藏  举报