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