【LOJ2127】「HAOI2015」按位或

题意

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

Solution

$ \text{min-max}$ 容斥。

答案即求 \(E(\max(S))\) 即全集 \(S\) 最后一个元素出现时间的期望。

根据 $ \text{min-max}$ 容斥 :

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

(注:下文中 \(S,T\) 并非上文中的 \(S,T\)
考虑求 \(E(\min(S))\) 即集合 \(S\) 第一个元素出现时间的期望。

\[E(\min(S)) = \frac{1}{\sum_{T\cap S \neq \emptyset}P_T} \]

考虑补集转化,设 \(S'\)\(S\) 的补集。

\[E(\min(S)) = \frac{1}{1-\sum_{T\subseteq S'}P_T} \]

直接高维前缀和即可。复杂度 \(O(2^n \cdot n)\)

code

#include<bits/stdc++.h>
const int N=(1<<20)+5;
double p[N],ans;
int main()
{
	int n; scanf("%d",&n);
	for(int i=0;i<(1<<n);++i)
	{
		scanf("%lf",&p[i]);
		if(fabs(p[i])<1e-7)
		{
			puts("INF");
			return 0;
		}
	}
	for(int i=0;i<n;++i)
		for(int j=0;j<(1<<n);++j)
			if(j&(1<<i)) p[j]+=p[j^(1<<i)];
	for(int i=1;i<(1<<n);++i)
	{
		double tmp=1.0/(1.0-p[i^((1<<n)-1)]);
		__builtin_popcount(i)&1?ans+=tmp:ans-=tmp;
	}
	printf("%.7lf",ans);
}

posted @ 2019-06-11 09:53  x_faraway_x  阅读(226)  评论(1编辑  收藏  举报