Description
刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal
的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1问期望多少秒后,你手上的数字变成2^n-1。
n≤20n\le20n≤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)=1P(T)E(T)=\frac{1}{P(T)}E(T)=P(T)1
考虑补集转化,记U=∁(T),U=\complement(T),U=∁(T),那么P(T)=1−∑S⊂Up(S)P(T)=1-\sum\limits_{S\subset U}{p(S)}P(T)=1−S⊂U∑p(S)
这里求∑T⊂Sp(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;
}

587

被折叠的 条评论
为什么被折叠?



