BZOJ 4036 [HAOI2015]按位或

快速莫比乌斯变换

关于集合幂级数的更多内容请看2015年集训队论文最后一篇VFK的。orz orz

主要思路就是把求一个集合幂级数的卷积转化成一个求子集和的问题。过程是作出莫比乌斯变换,变成求和,再快速莫比乌斯反演回去。

fi(s) i 次过后,并集为 s 的概率。记 h(s) 表示最终并集为 s 的期望次数,记U=2n1

那我们要求的就是

h(U)=i=1i[fi(U)fi1(U)]

对两边做莫比乌斯变换

h^(U)=i=1i[f^i(U)f^i1(U)]

我们希望快速求出右边,从而反演出左边,观察 f^i(U) 的定义式

f^i(U)=s1Ufi1(s1)s2Uf1(s2)

整合一下,有

f^i(U)=[f^1i(U)]i

因此,有

h^(U)=i=1i[f^1(U)]i[f^1(U)]i1]

右边的和式可以通过扰动法求出等比数列的和,显然是收敛的,即

h^(U)=1f^1(U)1

h^(U) 反演回 h(U) 即可得到答案

h(U)=sU1f^1(s)1(1)U1s1

一个细节是上述的等比数列求和时,若 f1(U)=1 时不能用直那样求和,否则会除以 0 . 正确做法是忽略它,因为它的和总是11=0

#include<cstdio>
#define N 21
using namespace std;
namespace runzhe2000
{
    int n, g[1<<N]; double f[1<<N], h;
    void main()
    {
        scanf("%d",&n); 
        int U = (1<<n)-1, V;
        g[0] = 1;
        for(int i = 0; i <= U; i++) 
        {
            scanf("%lf",&f[i]);
            i?g[i] = -g[i-(i&-i)]:0;
            f[i]?V|=i:0;
        }
        if(V!=U){puts("INF");return;}
        for(int i = 0; i < n; i++)
            for(int j = 1; j <= U; j++) 
                (j&(1<<i)) ? f[j] += f[j-(1<<i)] : 0;
        for(int i = 0; i < U; i++) h += g[i] / (f[i] - 1);
        printf("%.8lf\n",h);    
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值