[HAOI2015]按位或
题目描述
刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2n-1]的数字,与你手上的数字进行或(c++,c的|,pascal的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1问期望多少秒后,你手上的数字变成2n-1。
输入输出格式
输入格式:
第一行输入n表示n个元素,第二行输入2^n个数,第i个数表示选到i-1的概率
输出格式:
仅输出一个数表示答案,绝对误差或相对误差不超过1e-6即可算通过。如果无解则要输出INF
输入输出样例
输入样例#1:
2
0.25 0.25 0.25 0.25
输出样例#1:
2.6666666667
说明
对于100%的数据,n<=20
题解
\(min_max\)容斥+高维前缀和
先说一下一点前置知识
\(min\_max\)容斥:
对于一个集合\(S\),\(max(S)\)表示\(S\)集合中最大的元素,\(min(S)\)表示\(S\)集合中最小的元素
那么
\(max(S)=\sum_{T \subset S}(-1)^{|T|+1}min(T)\)
\(min(S)=\sum_{T\subset S}(-1)^{|T|+1}max(T)\)
这玩意儿具体说起来比较麻烦,大致就是每种子集的极小/大值互相都消掉了,最后就剩下了\(T=\{max(S)\}\)这一项且前面的容斥系数是1
还有就是如果一个东西每步出现的概率为\(p\),那么这个东西出现的期望步数就是\(\frac{1}{p}\)
然后再搞这个题:
首先\(min\_max\)容斥是可以用在期望中的
我们设\(E(max(S))\)表示集合\(S\)中期望出现时间最大的元素的出现时间,也就是这个集合所有元素都出现的期望时间
设\(E(min(S))\)表示集合\(S\)中期望出现时间最小的元素的出现时间
发现答案就是\(E(max(U))=\sum_{S\subset U}{(-1)^{|S|+1}E(min(S))}\)
那么我们就枚举\(U\)的子集\(S\)
我们要计算\(E(min(S))\)比较困难
但是因为\(E(min(S))\)要保证之前出现的元素集合不能与\(S\)有交
所以我们可以反面计数
计算\(S\)的补集出现的概率\(p\)
那么\(S\)出现的概率就是\(1-p\)
那么\(S\)的期望步数就是\(\frac{1}{1-p}\)
那么答案就是\(\sum_{S\subset U}\frac{(-1)^{|S|+1}}{1-p}\)
代码
#include<cstdio>
#include<algorithm>
const int M = (1 << 20) + 5 ;
const double EPS = 1e-10 ;
using namespace std ;
int n , lim , cnt[M] ;
double p[M] , f[M] , ans ;
int main() {
scanf("%d",&n) ; lim = (1 << n) ;
for(int i = 1 ; i < lim ; i ++) cnt[i] = cnt[i >> 1] + (i & 1) ;
for(int i = 0 ; i < lim ; i ++) scanf("%lf",&p[i]) ;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j < lim ; j ++)
if(j & (1 << (i - 1)))
p[j] += p[j ^ (1 << (i - 1))] ;
for(int i = 1 ; i < lim ; i ++)
if(1 - p[(lim - 1) ^ i] > EPS)
ans += ((cnt[i] & 1) ? 1 : -1) / (1 - p[(lim - 1) ^ i]) ;
if(ans < EPS) printf("INF\n") ; else printf("%.10lf\n",ans) ;
return 0 ;
}