1. 程式人生 > >BZOJ4036:按位或 (min_max容斥&FWT)(占位)

BZOJ4036:按位或 (min_max容斥&FWT)(占位)

一個數 開始 子集 誤差 inf 一行 100% printf 一秒

Description

剛開始你有一個數字0,每一秒鐘你會隨機選擇一個[0,2^n-1]的數字,與你手上的數字進行或(c++,c的|,pascal 的or)操作。選擇數字i的概率是p[i]。保證0<=p[i]<=1,Σp[i]=1問期望多少秒後,你手上的數字變成2^n-1。

Input

第一行輸入n表示n個元素,第二行輸入2^n個數,第i個數表示選到i-1的概率

Output

僅輸出一個數表示答案,絕對誤差或相對誤差不超過1e-6即可算通過。如果無解則要輸出INF

Sample Input

2
0.250.250.250.25

Sample Output

2.6666666667

HINT

對於100%的數據,n<=20

思路:可以min_max容斥來做,問題的關鍵就是求出得到所有子集X的期望F(X)就可以了,P(X)的概率為所有對X有貢獻的p[x]之和(x是所有和X有交集的x,即便x含有X沒有的部分);

我們倒過來求與X交集為空的部分的概率,這部分可以用高維前綴和來做。高維前綴和這裏還沒見到過,占位。代碼先碼了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<21;
double P[maxn],ans;int
N,sum,M; void dfs(int pos,int now,int cnt) { if(pos==N){ if(cnt>=1){ if(cnt&1) ans+=1.0/(1.0-P[(M-1)^now]); else ans-=1.0/(1.0-P[(M-1)^now]); } return ; } dfs(pos+1,now|(1<<pos),cnt+1); dfs(pos+1,now,cnt); } int main() { scanf(
"%d",&N); M=1<<N; for(int i=0;i<M;i++){ scanf("%lf",&P[i]); if(P[i]>0) sum|=i; } if(sum!=M-1) return puts("INF"),0; for(int i=1;i<M;i<<=1) for(int p=i<<1,j=0;j<M;j+=p) for(int k=0;k<i;++k) P[i+j+k]+=P[j+k]; dfs(0,0,0); printf("%.6lf\n",ans); return 0; }

BZOJ4036:按位或 (min_max容斥&FWT)(占位)