题目描述
刚开始你有一个数字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
分析:
设
为一次走到
的概率,那么,小于等于两次走到
的概率就是,
多次的也可以推出。
在第 次走到 的概率为 ,所以到 的期望 为
就是答案。
有一个叫做莫比乌斯变换的东西,大概就是设
。
然后就是一堆乱七八糟的东西。
我们回忆对或运算的
和
。
观察 ,当 从后面起第 位为 时,就加上了除了这位为 ,其他位全与 相同的数。而这些数又加上他们的自己不同的数。也就是说, 后 数组的第 位,就是他的子集的和,也就是 。这两个是同一个东西。
那么那些什么莫比乌斯变换与反演,完全可以看作是 操作和 操作。
那么我们再来计算答案,先把
做
操作得到
。这两个式子其实是一个东西,就好像点值多项式和系数多项式的区别,所以,
化简一下得到,
其中,除了第一项的系数是 ,其他全是 。
因为 ,且因为 ,所以 。
因此,当
时,那么
,第一项为
,后面项为
,即
。
当
时,
因为 ,所以 ,
所以 。
综上,我们先给 跑一次 ,然后求出 ,然后给 跑 ,就可以求出 了。设 ,复杂度是 ,和 是一样的。
代码:
#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]);
}