BZOJ 4036 [HAOI2015]按位或

来源:互联网 发布:电池模拟器软件 编辑:程序博客网 时间:2024/06/06 13:57

快速莫比乌斯变换

关于集合幂级数的更多内容请看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 21using 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();}
0 0
原创粉丝点击