题目
题目链接:https://www.luogu.com.cn/problem/P3175
刚开始你有一个数字 (0),每一秒钟你会随机选择一个 ([0,2^n-1]) 的数字,与你手上的数字进行或(C++,C 的 |
,pascal 的 or
)操作。选择数字 (i) 的概率是 (p_i)。保证 (0leq p_i leq 1),(sum p_i=1) 。问期望多少秒后,你手上的数字变成 (2^n-1)。
思路
设 (S) 为全集 ((S={x|0leq x<2^n})),设 (E(x)) 表示 (x) 被包含于得到的数字数字的期望次数,根据 min-max 容斥,有
[E(max(S))=sum_{Tin S}(-1)^{|T|+1}E(min(T))
]
其中 (E(max(S))=E(2^n-1)) 即为我们所求。
考虑如何求出 (E(min(T)))。显然 (E(min(T))) 等于“一直在 ([0,2^n-1]) 中随机取数,直到取到一个数 (x mathrm{and} T
eq varnothing) 的期望次数”。
设 (f(T)) 表示在 ([0,2^n-1]) 中随机取数,取到 (T) 子集内的数的期望。那么
[f(T)=frac{1}{sum_{xin T}p(x)}
]
这个东西就是子集卷积,直接上 FWT 即可。
那么
[E(min(T))=frac{(f(T mathrm{xor} S)E(min(T))+1)+1}{2}=frac{1}{1-f(T mathrm{xor} S)}
]
这样就在 (O(2^nn)) 内搞定了这道题。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=(1<<20)+10;
const double eps=1e-12;
int n,lim,bit[N];
double ans,p[N];
int main()
{
scanf("%d",&n);
lim=(1<<n);
for (int i=0;i<lim;i++)
scanf("%lf",&p[i]);
for (int k=1;k<lim;k<<=1)
for (int i=0;i<lim;i+=(k<<1))
for (int j=0;j<k;j++)
p[i+j+k]+=p[i+j];
for (int i=0;i<lim-1;i++)
{
if (1-p[i]<eps) return printf("INF"),0;
p[i]=1.0/(1-p[i]);
}
for (int i=1;i<lim;i++)
{
bit[i]=bit[i^(i&-i)]+1;
ans+=((bit[i]&1)?1.0:-1.0)*p[lim-1-i];
}
printf("%.10lf",ans);
return 0;
}