BZOJ 4036: [HAOI2015]按位或 Min-Max容斥

title

BZOJ 4036

LUOGU 3175

Description

刚开始你有一个数字0,每一秒钟你会随机选择一个 \([0,2^n-1]\) 的数字,与你手上的数字进行或(c++,c的|,pascal的or)操作。选择数字i的概率是 p[i]。保证 0<=p[i]<=1,Σp[i]=1 问期望多少秒后,你手上的数字变成 \(2^n-1\)

Input

第一行输入 n 表示 n 个元素,第二行输入 \(2^n\) 个数,第 i 个数表示选到 \(i-1\) 的概率

Output

仅输出一个数表示答案,绝对误差或相对误差不超过 \(1e-6\) 即可算通过。如果无解则要输出 \(INF\)

Sample Input

2
0.25 0.25 0.25 0.25

Sample Output

2.6666666667

HINT

对于100%的数据,n<=20

analysis

前置技能

\(Min-Max\)容斥:

\(Min(S)\) 表示集合 \(S\) 中的最小值,

\(Max(S)\) 表示集合 \(S\) 中的最大值,

那么有两个式子:
\[ Min(S)=\sum_{T\subset S} (-1)^{|T|+1}Max(T)\\ Max(S)=\sum_{T\subset S} (-1)^{|T|+1}Min(T) \]


那么这道题为什么要用到它呢?

因为我们可以用 \(Max(S)\) 表示集合 \(S\) 最后的 1 出现时间的期望,

\(Min(S)\) 表示集合 \(S\) 中最早的 1 出现时间的期望,

那么上面的两个式子也是支持的(不错的,\(Min-Max\) 容斥支持期望),

所以可以得出 答案为 \(Max(2^n-1)\)

然后我们发现,好像这个求 \(Max(S)\) 不是很好求,那...

正难则反,我们求 \(Min(S)\),如果我们有一次 \(rand\) 到了 \(T,T\cap S\not=\emptyset\),那么 \(S\) 中就有 1 了。

列式子的时候到了:
\[ Min(S) = \frac{1}{\sum_{T \cap S\neq \emptyset} p_T} \\ = \frac{1}{1-\sum_{T \cap S = \emptyset}p_T} \\ = \frac{1}{1-\sum_{T \cap (U-S) = T}p_T} \\ = \frac{1}{1-\sum_{T \subseteq (U-S)}p_T} \]
\(\sum_{T \subseteq (U-S)}p_T\) 是可以用 \(FWT(FMT)\) 求的,因此做一次 \(FWT_{or}\) 就好了。

时间复杂度为 \(O(2^nn)\)

code

#include<bits/stdc++.h>

const int maxn=20;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
}

using IO::read;

int r[1<<maxn];
double P[1<<maxn];
int main()
{
    int n;read(n);
    int N=1<<n;
    for (int i=0; i<N; ++i) scanf("%lf",&P[i]), r[i]=r[i>>1]+(i&1);
    for (int i=1; i<N; i<<=1)
        for (int p=i<<1,j=0; j<N; j+=p)
            for (int k=0; k<i; ++k) P[i+j+k]+=P[j+k];//FWT_or
    double ans=0.0;
    for (int i=1; i<N; ++i)
        if (1-P[(N-1)^i]>1e-8) ans+=((r[i]&1) ? 1 : -1) / (1-P[(N-1)^i]);
    if (ans<1e-10) puts("INF");
    else printf("%.10lf\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/G-hsm/p/11400984.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值