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;
    }
    
    
posted @ 2018-11-21 19:03  hec0411  阅读(185)  评论(1编辑  收藏  举报