[HAOI2015] 按位或
给你一个长度为 \(2^n\) 的序列 \(P\),从 \(0\) 开始标号,保证 \(\sum\limits_{i=0}^{2^n - 1} p_i = 1\)。
你是 \(0\),你需要进行若干次下面的操作变成 \(2^n - 1\):
-
从 \([0,2^n -1]\) 中随机选择一个整数 \(x\),选中 \(x\) 的概率为 \(p_x\)。
-
将自己按位或上 \(x\)。
求期望操作次数,若不能完成目标返回 INF,\(n \leq 20\)。
你不觉得知道了套路之后做题目很爽吗?
套路性的 min-max 容斥一下。
我们需要对于每个位置集合 \(S\),求出至少包含 \(S\) 中一个位置的期望操作次数 \(x\)。
考虑 \(x = \sum\limits_{i \geq 0} [x > i]\),则我们需要求 \(i\) 次操作后不包含 \(S\)。
显然,我们记剩余的位置集合对应的概率和为 \(P_0\),则 \(x = \sum\limits_{i = 0}^{+ \infty} P_0 ^ i\)。
只要 \(P_0\) 小于 \(1\),这个东西就是收敛的,答案是 \(\dfrac{1}{1-P_0}\)。
\(P_0\) 等于 \(1\),此时这个东西是 INF,我们统计 INF 的个数即可。
水紫,嘟嘟嘟。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
using namespace std;
double P[1<<20];
int popcount[1<<20];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<(1<<n);i++){
scanf("%lf",&P[i]);
}
popcount[0]=-1;
for(int i=1;i<(1<<n);i++){
popcount[i]=-popcount[i&(i-1)];
}
for(int i=0;i<n;i++){
for(int j=0;j<(1<<n);j++){
if(j&(1<<i)){
P[j]+=P[j-(1<<i)];
}
}
}
int cnt_INF=0;
double ans=0;
for(int i=1;i<(1<<n);i++){
double p=P[(1<<n)-1-i];
if(p==1){
cnt_INF+=popcount[i];
}
else{
ans+=popcount[i]/(1-p);
}
}
if(cnt_INF){
printf("INF");
}
else{
printf("%.14lf",ans);
}
return 0;
}

浙公网安备 33010602011771号