题目描述
刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1。问期望多少秒后,你手上的数字变成2^n-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
分析:
设
p1(i),i∈[0,2n)
p
1
(
i
)
,
i
∈
[
0
,
2
n
)
为一次走到
i
i
的概率,那么,小于等于两次走到的概率就是,
多次的也可以推出。
在第 k k 次走到的概率为 pk(i)−pk−1(i) p k ( i ) − p k − 1 ( i ) ,所以到 i i 的期望为
f(2n−1) f ( 2 n − 1 ) 就是答案。
有一个叫做莫比乌斯变换的东西,大概就是设
f^(i)=∑j⊂if(j)
f
^
(
i
)
=
∑
j
⊂
i
f
(
j
)
。
然后就是一堆乱七八糟的东西。
我们回忆对或运算的
Fwt
F
w
t
和
Dwt
D
w
t
。
观察 Fwt F w t ,当 i i 从后面起第位为 1 1 时,就加上了除了这位为,其他位全与 i i 相同的数。而这些数又加上他们的自己不同的数。也就是说,后 A A 数组的第位,就是他的子集的和,也就是 f^(i) f ^ ( i ) 。这两个是同一个东西。
那么那些什么莫比乌斯变换与反演,完全可以看作是 Fwt F w t 操作和 Dwt D w t 操作。
那么我们再来计算答案,先把
p1(i)
p
1
(
i
)
做
Fwt
F
w
t
操作得到
p^1(i)
p
^
1
(
i
)
。这两个式子其实是一个东西,就好像点值多项式和系数多项式的区别,所以,
化简一下得到,
其中,除了第一项的系数是 inf i n f ,其他全是 1 1 。
因为,且因为 p0(0)=1,p0(x)=0,(x>0) p 0 ( 0 ) = 1 , p 0 ( x ) = 0 , ( x > 0 ) ,所以 p^0(i)=1(x>=0) p ^ 0 ( i ) = 1 ( x >= 0 ) 。
因此,当
p^1(i)=1
p
^
1
(
i
)
=
1
时,那么
p^k(i)=1
p
^
k
(
i
)
=
1
,第一项为
inf
i
n
f
,后面项为
−inf
−
i
n
f
,即
f^(i)=0
f
^
(
i
)
=
0
。
当
p^1(i)∈[0,1)
p
^
1
(
i
)
∈
[
0
,
1
)
时,
因为 p^1(i)∈[0,1) p ^ 1 ( i ) ∈ [ 0 , 1 ) ,所以 p^inf(i)=0 p ^ i n f ( i ) = 0 ,
所以 f^(i)=1p^1(i)−1 f ^ ( i ) = 1 p ^ 1 ( i ) − 1 。
综上,我们先给 p p 跑一次,然后求出 f^ f ^ ,然后给 f^ f ^ 跑 Dwt D w t ,就可以求出 f f 了。设,复杂度是 O(k∗logk) O ( k ∗ l o g k ) ,和 O(n∗2n) O ( n ∗ 2 n ) 是一样的。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=1048580;
const double limit=1e-12;
using namespace std;
int n;
double p[maxn];
void fwt(double *a,int l,int r)
{
if (l==r) return;
int n=(r-l+1)/2,mid=l+n;
fwt(a,l,mid-1);
fwt(a,mid,r);
for (int i=l;i<mid;i++)
{
double u=a[i],v=a[i+n];
a[i+n]=u+v;
}
}
void dwt(double *a,int l,int r)
{
if (l==r) return;
int n=(r-l+1)/2,mid=l+n;
dwt(a,l,mid-1);
dwt(a,mid,r);
for (int i=l;i<mid;i++)
{
double u=a[i],v=a[i+n];
a[i+n]=v-u;
}
}
int main()
{
scanf("%d",&n);
n=1<<n;
for (int i=0;i<n;i++) scanf("%lf",&p[i]);
fwt(p,0,n-1);
for (int i=0;i<n;i++)
{
if (abs(p[i]-1)<limit) p[i]=0;
else p[i]=1/(p[i]-1);
}
dwt(p,0,n-1);
if (abs(p[n-1])<limit) printf("INF");
else printf("%.8lf",p[n-1]);
}