minmax反演。
题目的本质是求所有位上的1出现的期望中最大的那一个。
-
minmax反演的套路:(max{s}=sum_{T subset S}(-1)^{|T|+1}min{T})。
-
下面简单证明
(感性理解)一下:我们假设最大值为x(有多个最大值也一样的),然后我们的(T)集合分为包含了x与没包含x的。
我们先不考虑({x}与emptyset)。由于x是最大值,所以(T)的最小值不受x的影响,所以对于其中一个不包含x的集合({A}),他的最小值与({A}igcup{x})的最小值相同,并且他们的集合大小差1,所以他们抵消了。
然后(emptyset)对答案无贡献,({x})对答案的贡献为x,所以等式成立。
这个东西在期望中很好用。
-
对本题而言,(min{T})就是(T)集合中任意一个1出现的期望。设所有与(T)有交集(相同位上的1)的所有数的出现概率和为(sum)。(比如:二进制下(T=100),那么与(T)有交集的数为({100,101,110,111}))。那么(min{T}=frac{1}{sum})。
证明:
[设E=min{T}\ E=sumcdot1+(1-sum)cdot(1+E)\ E=frac{1}{sum} ]然后直接套用上面的公式就可以了。
关键是怎么求出(sum)。
不会FWT我们感觉直接求与一个集合有交的所有集合有些困难,于是我们“正难则反”,求它的补集。也就是说,对于集合(T)我们求出包含于(T)的补集的所有数的(sum),然后(1-sum)就是我们要的东西了。
求包含于某个集合的所有数的(sum)可以用(O(n*2^{n}))的递推(注意循环嵌套的顺序不能错了)。
代码:
#include<bits/stdc++.h> #define ll long long #define N 21 #define eps 1e-7 using namespace std; inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n; double sum[1<<N],ans; int main() { n=Get(); int maxx=(1<<n)-1; for(int i=0;i<(1<<n);i++) { double a; scanf("%lf",&a); sum[i]=a; } for(int i=1;i<=n;i++) { for(int s=0;s<(1<<n);s++) { if(s&(1<<i-1)) sum[s]+=sum[s^(1<<i-1)]; } } for(int s=1;s<(1<<n);s++) { double f=-1; for(int i=1;i<=n;i++) if(s&(1<<i-1)) f*=-1; if(1-sum[maxx^s]<=eps) {cout<<"INF";return 0;} ans+=f/(1-sum[maxx^s]); } cout<<fixed<<setprecision(9)<<ans; return 0; }