洛谷 P3175 [HAOI2015]按位或 fwt

题目描述

刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=1。问期望多少秒后,你手上的数字变成2^n-1。

输入输出格式

输入格式:
第一行输入n表示n个元素,第二行输入2^n个数,第i个数表示选到i-1的概率

输出格式:
仅输出一个数表示答案,绝对误差或相对误差不超过1e-6即可算通过。如果无解则要输出INF

输入输出样例

输入样例#1:
2
0.25 0.25 0.25 0.25
输出样例#1:
2.6666666667
说明

对于100%的数据,n<=20

分析:
p 1 ( i ) , i [ 0 , 2 n ) 为一次走到 i 的概率,那么,小于等于两次走到 i 的概率就是,

p 2 ( i ) = j | k = i p 1 ( j ) p 1 ( k )

多次的也可以推出。
在第 k 次走到 i 的概率为 p k ( i ) p k 1 ( i ) ,所以到 i 的期望 f ( i )
f ( i ) = k = 1 i n f k ( p k ( i ) p k 1 ( i ) )

f ( 2 n 1 ) 就是答案。

有一个叫做莫比乌斯变换的东西,大概就是设 f ^ ( i ) = j i f ( j )
然后就是一堆乱七八糟的东西。
我们回忆对或运算的 F w t D w t

F w t ( A ) = ( F w t ( A 0 ) , F w t ( A 0 + A 1 ) )

D w t ( A ) = ( D w t ( A 0 ) , D w t ( A 1 A 0 ) )

观察 F w t ,当 i 从后面起第 j 位为 1 时,就加上了除了这位为 0 ,其他位全与 i 相同的数。而这些数又加上他们的自己不同的数。也就是说, F w t A 数组的第 i 位,就是他的子集的和,也就是 f ^ ( i ) 。这两个是同一个东西。
那么那些什么莫比乌斯变换与反演,完全可以看作是 F w t 操作和 D w t 操作。

那么我们再来计算答案,先把 p 1 ( i ) F w t 操作得到 p ^ 1 ( i ) 。这两个式子其实是一个东西,就好像点值多项式和系数多项式的区别,所以,

f ^ ( i ) = k = 1 i n f k ( p ^ k ( i ) p ^ k 1 ( i ) )

= ( p ^ 1 ( i ) p ^ 0 ( i ) ) + 2 ( p ^ 2 ( i ) p ^ 1 ( i ) ) + . . . . . . .

化简一下得到,
= i n f p ^ i n f ( i ) . . . p ^ 1 ( i ) p ^ 0 ( i )

其中,除了第一项的系数是 i n f ,其他全是 1
因为 p ^ 1 ( i ) [ 0 , 1 ] ,且因为 p 0 ( 0 ) = 1 , p 0 ( x ) = 0 , ( x > 0 ) ,所以 p ^ 0 ( i ) = 1 ( x >= 0 )

因此,当 p ^ 1 ( i ) = 1 时,那么 p ^ k ( i ) = 1 ,第一项为 i n f ,后面项为 i n f ,即 f ^ ( i ) = 0
p ^ 1 ( i ) [ 0 , 1 ) 时,

f ^ ( i ) = i n f p ^ i n f ( i ) ( + . . . + p ^ 1 ( i ) + p ^ 0 ( i ) )

= i n f p ^ i n f ( i ) p ^ i n f ( i ) p ^ 0 ( i ) p ^ 1 ( i ) 1

因为 p ^ 1 ( i ) [ 0 , 1 ) ,所以 p ^ i n f ( i ) = 0
所以 f ^ ( i ) = 1 p ^ 1 ( i ) 1

综上,我们先给 p 跑一次 F w t ,然后求出 f ^ ,然后给 f ^ D w t ,就可以求出 f 了。设 k = 2 n ,复杂度是 O ( k l o g k ) ,和 O ( n 2 n ) 是一样的。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>

const int maxn=1048580;
const double limit=1e-12;

using namespace std;

int n;
double p[maxn];

void fwt(double *a,int l,int r)
{
    if (l==r) return;
    int n=(r-l+1)/2,mid=l+n;
    fwt(a,l,mid-1);
    fwt(a,mid,r);
    for (int i=l;i<mid;i++)
    {
        double u=a[i],v=a[i+n];
        a[i+n]=u+v;
    }
}

void dwt(double *a,int l,int r)
{
    if (l==r) return;
    int n=(r-l+1)/2,mid=l+n;
    dwt(a,l,mid-1);
    dwt(a,mid,r);
    for (int i=l;i<mid;i++)
    {
        double u=a[i],v=a[i+n];
        a[i+n]=v-u;
    }
}

int main()
{
    scanf("%d",&n);
    n=1<<n;
    for (int i=0;i<n;i++) scanf("%lf",&p[i]);
    fwt(p,0,n-1);   
    for (int i=0;i<n;i++)
    {
        if (abs(p[i]-1)<limit) p[i]=0;
                        else p[i]=1/(p[i]-1);
    }
    dwt(p,0,n-1);
    if (abs(p[n-1])<limit) printf("INF");
                      else printf("%.8lf",p[n-1]);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81712547
今日推荐