[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;
}
posted @ 2026-01-14 09:33  Oken喵~  阅读(1)  评论(0)    收藏  举报