[HAOI2015]按位或
Link
Description
\(n\) 个元素,一共构成 \(2^n\) 个集合,每个集合 \(s\) 有一个被选中的概率 \(p_s\),保证 \(\sum_{s} p_s=1\)。选中一个集合后,集合里所有元素的 tag 变为 1。问期望选多少次集合后,所有元素的 tag 都是 1。
Solution
一开始有一个并不正确的做法,认为每一位是独立的,然后计算每一位被打上 tag 的期望时间,然后对所有时间取 max。可以这样理解这个做法的错误所在,期望是对所有情况的平均估计,有些情况下,即使当前这个期望时间最大的元素被打上了 tag,也可能有其他元素没有。所以直接取最大时间是错的。
正确做法应该是将所有元素当做整体来考虑。\(E(max\{S\})\) 表示集合 \(S\) 中最后一个元素被打上 tag 的期望时间。
考虑 min-max 容斥,有
\[E(max\{S\})=\sum_{T\subseteq S} (-1)^{|T|-1} E(min\{T\})
\]
\(E(min\{S\})\) 即为 \(S\) 中至少有一个元素被打上标记的期望时间。如果能快速求这个,那么 \(E(max\{S\})\) 就能通过 \(O(2^n)\) 的暴力枚举求出。
容易发现有
\[E(min\{S\})=1+E(min\{S\})\sum_{S \cap T = \emptyset}p_T
\]
整理得到
\[E(min\{S\})=\frac{1}{1-\sum_{S \cap T = \emptyset}p_T}
\]
\(S\) 和 \(T\) 交集为空,那么 \(T\) 一定是 \(S\) 的补集的子集,所以总系数就是所有子集的概率求和。这个东西可以通过高位前缀和 \(O(n2^n)\) 预处理出来。那么这道题就做完了,答案就是 \(E(max\{U\})\)
#include<stdio.h>
#define db double
const int N=20;
int n,p,cnt[1<<N];
db a[1<<N],ans=0;
int main(){
scanf("%d",&n); p=1<<n;
for(int i=0;i<p;i++) scanf("%lf",&a[i]),cnt[i]=cnt[i>>1]+(i&1);
for(int j=0;j<n;j++)
for(int i=0;i<p;i++)
if((1<<j)&i) a[i]+=a[i^(1<<j)];
for(int i=1;i<p;i++) if(1-a[(p-1)^i]>1e-8) ans+=((cnt[i]&1)? 1:-1)/(1-a[(p-1)^i]);
if(ans>1e-8) printf("%.8lf",ans);
else printf("INF");
}