LOJ#2127「HAOI2015」按位或
用$ Min-Max$容斥之后要推的东西少了好多
无耻的用实数快读抢了BZOJ、Luogu、LOJ三个$ OJ$的Rank 1
即将update:被STO TXC OTZ超了QAQ
题意:集合$ [0,2^n)$中每次以一定给出概率产生一个数,求产生数按位或值为$ 2^n-1$的数字数量期望
$ Solution:$
根据$ Min-Max$容斥,令$ Max(S)$表示所有位中最后一次出现的时间,$Min(S)$表示第一次出现的时间
显然有$ ans=Max(S)$
根据$ Min-Max$容斥有
$ Max(S)=\sum\limits(-1)^{|T|+1}Min(T)$
现在的问题是如何快速求出$ Min(S)$
由于$ Min(S)$的意义是集合$ S$中第一次出现的位置的出现时间的期望
有每次随机到集合$ S$中某一位的概率为:
$ \sum\limits_{T \cap S \neq \emptyset}P_T$
其中$ P_T$是产生$ T$这个数的概率
因此可以得知
$ Min(S)=\frac{1}{\sum\limits_{T \cap S \neq \emptyset}P_T}$
其中和原集有交可以转化成不属于原集的补集
求原集的补集可以用$ FMT/FWT$优化,时间复杂度$ O(n·2^n)$
$ my \ code:$
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define rt register int #define ll long long using namespace std; inline ll read(){ ll x = 0; char zf = 1; char ch = getchar(); while (ch != '-' && !isdigit(ch)) ch = getchar(); if (ch == '-') zf = -1, ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int i,j,k,m,n,x,y,z,cnt,tot; double a[1048577]; namespace fast_IO{ const int IN_LEN=10000000,OUT_LEN=10000000; char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1; inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;} inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;} inline void flush(){fwrite(obuf,1,oh-obuf,stdout);} } using namespace fast_IO; #define getchar() getchar_() double readf(){ char ch=getchar(); while(!isdigit(ch))ch=getchar(); double value=ch-'0'; ch=getchar(); while(isdigit(ch)){ value=value*10+ch-'0'; ch=getchar(); } if(ch=='.'){ double cur=0.1; ch=getchar(); while(isdigit(ch)){ value+=cur*(ch-'0'); cur*=0.1; ch=getchar(); } } return value; } int main(){ tot=read();n=1<<tot; for(rt i=0;i<n;i++)a[i]=readf(); for(rt i=0;i<tot;i++) for(rt j=0;j<n;j++)if(j>>i&1)a[j]+=a[j^(1<<i)]; double ans=0; for(rt i=0;i<tot;i++)if(a[n-1^(1<<i)]==1)return puts("INF"),0; for(rt i=0;i<n;i++)if(a[i]<1-0.0000001){ if(__builtin_popcount(i)+tot&1) ans+=1.0/(1.0-a[i]);else ans-=1.0/(1.0-a[i]); } printf("%.10f",ans); return 0; }