【HAOI2015】【容斥】【FMT】按位或

【描述】
剛開始你有一個數字 0,每一秒鐘你會隨機選擇一個[0,2n1][0,2^n −1]的數字,與你手上的數字進行或(C++, C 的 |, Pascal 的 or)操作。選擇數字 i 的概率是 p[i](保證 0p[i]1,p[i]=10≤p[i]≤1, ∑p[i]=1) 問期望多少秒後,你手上的數字變成2n12^n-1
【輸入】
第一行輸入 n 表示 n 個元素,第二行輸入2n2^n個數,第 i 個數表示選到 i−1的概率。
【輸出】
僅輸出一個數表示答案,絕對誤差或相對誤差不超過 10610^{-6} 即可算通過。如果無解則要輸出INF

【思路】

首先,我們要求的就是最晚出現的一個1出現的時間的期望。考慮minmax容斥,我們要求全集的每個子集S最早的一個1出現的期望,容斥係數爲(1)S1(-1)^{|S|-1}。即我們選出的數的與S有交集的期望時間,記這個概率爲P,那麼期望爲1P\frac{1}{P}。而這個概率加上每個與S沒有交集的數出現的概率之和就是1,所以我們計算出每個與S沒有交集的數出現的概率之和。這是一個子集求和,FMT即可。
代碼:

#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=2e6+5;
int U=0,siz[N],n,m,a,b,c;
inline void FMT(double *f)
{
	for(int re i=0;i^n;i++)
		for(int re j=0;j^U;j++)
			if(j&(1<<i))f[j]+=f[j^(1<<i)];
}
double ans=0,f[N];
int s[2]={-1,1};
int main()
{
	scanf("%d",&n);U=(1<<n);
	for(int re i=0;i^U;i++)scanf("%lf",&f[i]);
	for(int re i=0;i^U;i++)
		for(int re j=0;j^n;j++)
			if(i&(1<<j))++siz[i];
	FMT(f);
	for(int re i=1;i^U;i++)
	{
		if(1.0-f[i^(U-1)]<1e-10)return puts("INF"),0;
		f[i^(U-1)]=1.0/(1.0-f[i^(U-1)]);
		ans+=s[siz[i]&1]*f[i^(U-1)];
	}
	printf("%.6f",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章