【BZOJ4036】按位或(HAOI2015)-Min-Max容斥+FWT

测试地址:按位或
做法:本题需要用到Min-Max容斥+FWT。
因为一直是或,所以一个位置上如果有了1,这个1就会一直有下去,那么问题就变成了,每次选择一个子集,问所有点都被选过的期望次数。所有点都被选过的期望次数,也就相当于这些点中最后一个点被选的期望次数,容易想到Min-Max容斥:
E[max{S}]=TS(1)|T|+1E[min{T}]
那么现在问题就变成如何求E[min{T}]了。注意到这个期望相当于,这些点中第一次被取到的期望次数,也就等于这些点被取到的概率的倒数。直接统计被取到的概率较难,考虑补集转化,变成求不被取到的概率,容易发现这就是所有ST的子集的概率和,记为P(ST)。于是问题就变成快速求出所有集合的P
显然直接枚举计算是O(3n)的,无法承受。这时我们看到标题里有一个FWT(这个引入太牵强了吧……),可是这里并没有出现位运算卷积之类的东西啊……
别被套进去了,谁说FWT只是拿来求位运算卷积的了?在关于或运算的FWT中,我们通过正变换可以得到bi=j|i=iaj。等等,仔细一想,这不就是求集合i所有子集jaj和吗?于是我们用FWT来解决这个问题,时间复杂度为O(n2n),而后面枚举集合就是O(2n)的了,可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
int m,n;
double p[1500010];

void FWT(double *a)
{
    for(int mid=1;mid<n;mid<<=1)
        for(int l=0;l<n;l+=(mid<<1))
            for(int k=0;k<mid;k++)
            {
                double x=a[l+k],y=a[l+mid+k];
                a[l+k]=x;
                a[l+mid+k]=x+y;
            }
}

int main()
{
    scanf("%d",&m);
    n=(1<<m);
    for(int i=0;i<n;i++)
        scanf("%lf",&p[i]); 

    FWT(p);
    double ans=0.0;
    for(int i=1;i<n;i++)
    {
        double cnt=-1.0;
        int x=i;
        while(x) x-=(x&(-x)),cnt=-cnt;
        if (1.0-p[n-i-1]<1e-8) {printf("INF");return 0;}
        ans+=cnt/(1.0-p[n-i-1]);
    }
    printf("%.10lf",ans);

    return 0;
}
posted @ 2018-07-06 18:42  Maxwei_wzj  阅读(121)  评论(0编辑  收藏  举报