题意:有一台机巧的机器,每次会输出一个 [0,2n−1] 的数。输出i的概率为p[i],问平均多少次,输出的数或起来为 2n−1 。
还是论文里讲的比较清楚。
用集合并卷积定义集合幂级数乘法。
即
(f∗g)[S]=∑L∪R=Sf[L]∗g[R]
U表示全集,即 2n−1
经过简单的期望推导
ans=∑k=1∞k[(pk)U−(pk−1)U]
设一个函数f,使得
fS=∑k=1∞k[(pk)S−(pk−1)S]
两边同时莫比乌斯变换(不会打符号的蒟蒻)
ffS=∑k=1∞k[(ppk)S−(ppk−1)S]=∑k=1∞k[ppkS−ppk−1S]
我还以为要用到什么机巧的知识,用两节数学课复习了第二本书最后一章,并没有看出什么。
用脚推一下才发现这就是上学期学的 错位相减。
由于概率和为1, ppS ≤1。若 ppS =1, ffS=0 。否则
ffS=−∑k=0∞ppkS=−11−ppS
把ff每一项算出来,再莫比乌斯反演回去,就得到答案了。
莫比乌斯变化就是维长为2的多维前缀和,反演就是差分,我是用dp理解的。
但为了好记,我不用dp的写法,把它和快速沃尔什变换写在一起(虽然我不知道那是什么操作)。
难道那么长的论文,让我记住了那么多符号,还深感智商捉急,难道就比不上这样一个表吗
就像fft一样
蝴蝶操作什么的,已经没所谓,。
因为已经不再有公式,值得我去推了。
看不懂的论文,已经不需要了,
因为已经不再有黑科技,值得我去学了。
真不怕被打死,嘟嘟噜
所以说记住上面的表和3个for,就能切掉别人用集合幂级数秀你一脸的题了。
(其实关键还是要找到卷积)
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const int N=2002000;
const double eps=1e-5;
int nn,n;
double f[N],a[N];
bool ok[21];
void fwt(double *a,int ops)
{
for(int k=1;k<n;k<<=1)
for(int m=k<<1,i=0;i<n;i+=m)
for(int j=0;j<k;j++)
{
double x=a[i+j],y=a[i+j+k];
if(ops)
a[i+j+k]=x+y;
else
a[i+j+k]=y-x;
}
}
int main()
{
cin>>nn;
n=(1<<nn);
for(int i=0;i<n;i++)
{
scanf("%lf",&a[i]);
if(a[i]>0.0)
for(int j=0;j<nn;j++)
if((1<<j)&i)
ok[j]=1;
}
for(int j=0;j<nn;j++)
if(!ok[j])
{
puts("INF");
return 0;
}
fwt(a,1);
for(int i=0;i<n;i++)
if(a[i]!=1.0)
f[i]=-1/(1.0-a[i]);
fwt(f,0);
printf("%.10lf\n",f[n-1]);
return 0;
}