BZOJ4036:按位或(快速莫比乌斯变换)

题面

题意:有一台机巧的机器,每次会输出一个 [0,2n1] 的数。输出i的概率为p[i],问平均多少次,输出的数或起来为 2n1

还是论文里讲的比较清楚。
用集合并卷积定义集合幂级数乘法。

(fg)[S]=LR=Sf[L]g[R]

U表示全集,即 2n1
经过简单的期望推导
ans=k=1k[(pk)U(pk1)U]

设一个函数f,使得
fS=k=1k[(pk)S(pk1)S]

两边同时莫比乌斯变换(不会打符号的蒟蒻)
ffS=k=1k[(ppk)S(ppk1)S]=k=1k[ppkSppk1S]

我还以为要用到什么机巧的知识,用两节数学课复习了第二本书最后一章,并没有看出什么。
用脚推一下才发现这就是上学期学的 错位相减
由于概率和为1, ppS ≤1。若 ppS =1, ffS=0 。否则

ffS=k=0ppkS=11ppS

把ff每一项算出来,再莫比乌斯反演回去,就得到答案了。

莫比乌斯变化就是维长为2的多维前缀和,反演就是差分,我是用dp理解的。
但为了好记,我不用dp的写法,把它和快速沃尔什变换写在一起(虽然我不知道那是什么操作)。

难道那么长的论文,让我记住了那么多符号,还深感智商捉急,难道就比不上这样一个表吗
这里写图片描述

就像fft一样
蝴蝶操作什么的,已经没所谓,。
因为已经不再有公式,值得我去推了。
看不懂的论文,已经不需要了,
因为已经不再有黑科技,值得我去学了。
真不怕被打死,嘟嘟噜

所以说记住上面的表和3个for,就能切掉别人用集合幂级数秀你一脸的题了。
(其实关键还是要找到卷积)

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=2002000;
const double eps=1e-5;

int nn,n;
double f[N],a[N];
bool ok[21];

void fwt(double *a,int ops)
{
    for(int k=1;k<n;k<<=1) 
    for(int m=k<<1,i=0;i<n;i+=m)
    for(int j=0;j<k;j++)
    {
        double x=a[i+j],y=a[i+j+k];
        if(ops)
        a[i+j+k]=x+y;   
        else
        a[i+j+k]=y-x;
    }
}

int main()
{
    cin>>nn;
    n=(1<<nn);

    for(int i=0;i<n;i++)
    {
        scanf("%lf",&a[i]);
        if(a[i]>0.0)
        for(int j=0;j<nn;j++)
        if((1<<j)&i)
        ok[j]=1;
    }

    for(int j=0;j<nn;j++)
    if(!ok[j])
    {
        puts("INF");
        return 0;
    }

    fwt(a,1); 

    for(int i=0;i<n;i++)
    if(a[i]!=1.0)
    f[i]=-1/(1.0-a[i]);

    fwt(f,0);

    printf("%.10lf\n",f[n-1]);

    return 0;
}

这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值