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

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值