题目描述
刚开始你有一个数字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_{Tsubset 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_{Ssubset 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_{Ssubset 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
") ; else printf("%.10lf
",ans) ;
return 0 ;
}