Description
刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal
的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1问期望多少秒后,你手上的数字变成2^n-1。
n
≤
20
n\le20
n≤20
Solution
考虑min-max容斥,我们枚举一次就选中的位记为T,问题在于求E(T)
不妨记
P
(
T
)
=
∑
R
∣
T
≠
∅
p
(
R
)
P(T)=\sum\limits_{R|T\neq\empty}{p(R)}
P(T)=R∣T̸=∅∑p(R),显然
E
(
T
)
=
1
P
(
T
)
E(T)=\frac{1}{P(T)}
E(T)=P(T)1
考虑补集转化,记
U
=
∁
(
T
)
,
U=\complement(T),
U=∁(T),那么
P
(
T
)
=
1
−
∑
S
⊂
U
p
(
S
)
P(T)=1-\sum\limits_{S\subset U}{p(S)}
P(T)=1−S⊂U∑p(S)
这里求
∑
T
⊂
S
p
(
T
)
\sum\limits_{T\subset S}p(T)
T⊂S∑p(T)也就是子集和,实际上直接做一次FWT就可以了
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
const int N=1049576;
double p[N];
int c[N];
void FWT(double *a,int n) {
for (int i=1;i<n;i<<=1) {
for (int j=0;j<n;j+=(i<<1)) {
for (int k=0;k<i;++k) {
a[j+k+i]+=a[j+k];
}
}
}
}
int main(void) {
freopen("data.in","r",stdin);
int n; scanf("%d",&n);
int lim=1<<n;
for (int i=0;i<lim;++i) {
scanf("%lf",&p[i]);
c[i]=c[i>>1]^(i&1);
}
FWT(p,lim);
double ans=0;
for (int i=1;i<lim;++i) {
if ((1-p[(lim-1)^i])>1e-8) {
ans+=(c[i]?(1.0):(-1.0))/(1-p[(lim-1)^i]);
}
}
if (ans<1e-8) puts("INF");
else printf("%.10lf\n", ans);
return 0;
}