CF698C题解
阅读原文时间:2023年07月08日阅读:2

为什么 \(n,k \leq 20\)?

我还以为是什么 \(n,k \leq 10^6\) 的厉害题/qd

看到这个队列操作很迷惑,但是仔细看看要操作 \(10^{100}\) 遍,所以我们可以直接将这个题意理解成在 \(n\) 个数里面选 \(k\) 个数的概率。

这就很简单了,因为 \(n \leq 20\),所以我们直接大力枚举包含 \(i\) 的子集,然后计算选到这个子集的概率,然后就没了。

这个东西很容易用子集 dp 来转移,子集的概率之和也可以使用 FWT 来计算。

记得特判某些情况会输出 nan

#include<cstdio>
#include<cmath>
int n,x,S,cnt,siz[1<<20];double ans[20],s[1<<20],P[1<<20];
signed main(){
    int i,k,len;
    scanf("%d%d",&n,&x);S=1<<n;s[0]=1;
    for(i=0;i^n;++i)scanf("%lf",P+(1<<i)),cnt+=P[1<<i]!=0;if(cnt<x)x=cnt;
    for(len=1;len^S;len<<=1)for(k=0;k^S;k+=len<<1)for(i=0;i^len;++i)P[i|k|len]+=P[i|k];
    for(i=0;i^S;++i){
        siz[i]=siz[i>>1]+(i&1);
        for(k=0;k^n;++k)if(i>>k&1)s[i]+=s[i^1<<k]*P[1<<k]/P[S-1^i^1<<k];
        if(siz[i]==x)for(k=0;k^n;++k)if(i>>k&1)ans[k]+=s[i];
    }
    for(i=0;i^n;++i)printf("%.9lf ",ans[i]);
}