大致题意: 每个单位时间有(p_i)的概率选中(i∈[0,2^n-1]),求期望多少时间后选中的数按位或的结果变成了(2^n-1)。
前言
典型的数学题:看起来很吓人的知识点+似乎很高深的分析+短短几行的代码。。。
分析
要做这道题,首先我们要知道一个叫做(Min-Max)容斥的东西:
[E(max(S))=sum_{Tsubseteq S}(-1)^{|T|+1}E(min(T))
]
具体地,在这题中,(E(min(T)))表示(T)中出现任意一位的期望时间,(E(max(S)))表示(S)中所有位都出现的期望时间(这里的(S)即为(2^n-1))。
换言之,我们只要能求出(E(min(T))),就可以在(O(2^n))的时间复杂度内通过容斥求出(E(max(S)))。那么我们只需要考虑如何求一个集合中出现任意一位的期望时间。
首先自然有一个很暴力的式子:
[E(min(T))=frac1{sum_{i∩T
ot=varnothing}p_i}
]
这个式子的关键是分母中的概率,且这个东西显然不好维护。因此我们反过来考虑:出现任意一位的概率就是(1-)不出现任意一位的概率。
也即是说:
[E(min(T))=frac1{1-sum_{i∩T=varnothing}p_i}
]
令(k=T xor (2^n-1))(即(T)的补集),那么这个式子就相当于是:
[E(min(T))=frac1{1-sum_{isubseteq k}p_i}
]
显然(sum_{isubseteq k}p_i)可以用(FWT)高效地求出,于是这题就做完了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 20
#define DB double
using namespace std;
int n,s,op[1<<N];DB p[1<<N];
int main()
{
RI i,g=0;for(scanf("%d",&n),s=1<<n,i=0;i^s;++i) scanf("%lf",p+i);//读入
for(i=0;i^s;++i) p[i]&&(g|=i);if(g^(s-1)) return puts("INF"),0;//判无解
RI j,k;for(i=1;i^s;i<<=1) for(j=0;j^s;j+=i<<1) for(k=0;k^i;++k) p[i+j+k]+=p[j+k];//FWT
DB t=0;for(op[0]=-1,i=0;i^s;++i)//Min-Max容斥
op[i]=op[i>>1]*(i&1?-1:1),p[i^(s-1)]<1-1e-8&&(t+=op[i]/(1-p[i^(s-1)]));
return printf("%.10lf
",t),0;
}