正题
我们探究着反演的奥妙,突然发现有一条式子,很奇怪:
接着,有一群人就把它们推广成普遍的式子:
我们就可以做很多事情了:
比如说遇到一些max值很难求出来的,而且min值很容易得到的,我们可以通过Min-Max反演来枚举子集使得其算出max值。
就像这一题:[HAOI2015]按位或
我们先把每一位看成一个点,点的权值就是这个点被选中的期望时间,又因为是或操作,所以被操作成1就不可能变回去了。
发现这个答案相当于求点权中的的最大值。利用Min-Max反演,把它转换为求全集的所有子集的min值,再算出答案。
那么min值就相当于一个子集中至少一个点被操作的期望,转化为1/一个子集中至少一个点被操作的概率,接着,我们运用补集转化,把它转化为1-这个子集的补集的子集的概率和。发现这个东西可以直接做一遍FWT的或的正变换。(相当于每一个位置上的值变为其所有子集的和)
然后就做完了?记得判断INF情况!
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n,limit;
double p[1<<20];
int pc[1<<20];
double eps=1e-8;
double ans=0;
int main(){
scanf("%d",&n);limit=(1<<n);
for(int i=0;i<limit;i++) scanf("%lf",&p[i]);
for(int i=0;i<limit;i++) pc[i]=pc[i>>1]+(i&1);
for(int l=2;l<=limit;l*=2)
for(int i=0;i<limit;i+=l)
for(int x=i,y=i+l/2;y<i+l;x++,y++)
p[y]+=p[x];
for(int i=0;i<limit-1;i++)
if(p[i]+eps>1) {printf("INF");return 0;}
for(int i=1;i<limit;i++)
ans+=1/(1-p[limit-1-i])*((pc[i]&1)?1:-1);
printf("%.8lf",ans);
}