Luogu P3175 [HAOI2015]按位或
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\}\bigcup\{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=sum\cdot1+(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; }