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

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

#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;
}

猜你喜欢

转载自blog.csdn.net/Maxwei_wzj/article/details/80945095