P3175 [HAOI2015]按位或
题目大意:
刚开始你有一个数字 \(0\),每一秒钟你会随机选择一个 \([0,2^n-1]\) 的数字,与你手上的数字进行或(C++,C 的 |
,pascal 的 or
)操作。选择数字 \(i\) 的概率是 \(p_i\)。保证 \(0\leq p_i \leq 1\),\(\sum p_i=1\) 。问期望多少秒后,你手上的数字变成 \(2^n-1\)。
仅输出一个数表示答案,绝对误差或相对误差不超过 \(10^{-6}\) 即可算通过。如果无解则要输出 INF
。
样例 #1
样例输入 #1
2
0.25 0.25 0.25 0.25
样例输出 #1
2.6666666667
\(n\leq 20\)
总结:一道比较经典的\(Min\_Max\)容斥题
我们设集合\(U\)为所有二进制位的全集
题目等价于求\(E(Max(U))\)
这个东西显然不是很好求,我们考虑求解\(E(Min(S))\)
\(E=\sum val \times p\)(期望=概率 \(\times\) 值)
我们考虑设\(P(S)\)表示选中集合\(S\)其中一个的概率,\(Q(S)\)表示选中\(S\)的补集的子集的概率
设\(Min(S)=k\),那么就需要在前\(k-1\)次选\(S\)的补集的子集,在第\(k\)次不选\(S\)的补集的子集
\(E(Min(S))=\sum_{k=1}^{\infty} k \times Q(S)^{k-1}(1-Q(S))\)
设\(Q(S)=x\)
\(E(Min(S))=(1-x)\sum_{k=1}^{\infty} k \times x^{k-1}\)
\(E(Min(S))=(1-x)(1 \times 1 + 2 \times x + 3 \times x^2...)\)
我们观察后面那个式子,形式是一个等差\(\times\)等比的数列
我们考虑对后面的式子求和
设\(f(x)=1 \times 1 + 2 \times x + 3 \times x^2...\)
\(xf(x)=1 \times x + 2 \times x^2 + 3 \times x^3...\)
两式相减,\((1-x)f(x)=1+x+x^2+x^3+....=\frac{1}{1-x}\)
\(\therefore E(Min(S))=\frac{1}{1-Q(S)}\)
所以我们要求出\(E(Min(S))\),我们首先要求出\(Q(S)\)
不难发现,\(Q(S)=\sum_{T\in S} P(S)\)
时间复杂度为\(O(3^n)\),直接算显然会超时
观察求和的形式很像求子集,于是我们就用高位前缀和进行优化,可以优化到\(O(n \times 2^n)\)
代码:
#include<bits/stdc++.h>
#define ll long long
#define int long long
#define fr(i,j,k) for(register int i=j;i<=k;++i)
#define rf(i,j,k) for(register int i=j;i>=k;--i)
#define foredge(i,j) for(register int i=head[j];i;i=e[i].nxt)
#define randfind(l,r) (rand()%((r)-(l)+1)+(l))
#define pb push_back
#define Times printf("Time:%.3lf\n",clock()/CLOCKS_PER_SEC)
using namespace std;
inline int read(){
int x=0;
bool f=0;
char c=getchar();
while(!isdigit(c)) f|=(c=='-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return f?-x:x;
}
inline ll Read(){
ll x=0;
bool f=0;
char c=getchar();
while(!isdigit(c)) f|=(c=='-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return f?-x:x;
}
inline void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline void writeln(int x){write(x); puts("");}
inline void writepl(int x){write(x); putchar(' ');}
inline void Write(ll x){
if(x<0){putchar('-'); x=-x;}
if(x>9)Write(x/10);
putchar(x%10+'0');
}
inline void Writeln(ll x){Write(x); puts("");}
inline void Writepl(ll x){Write(x); putchar(' ');}
const int Maxn=(1<<20)+10,mod=998244353,inv2=499122177;
const double cor[2][2]={{1.0,0.0},{1.0,1.0}},
icor[2][2]={{1.0,0.0},{-1.0,1.0}},
cand[2][2]={{0,1},{1,1}};
int n;
double a[Maxn],b[Maxn],c[Maxn];
double aa[Maxn],bb[Maxn];
inline void fwt(double *f,int len,const double h[2][2]){
for(register int k=2;k<=len;k*=2){
for(register int i=0;i<len;i+=k){
for(register int j=i;j<i+k/2;j++){
double x,y;
x=f[j];
y=f[j+k/2];
// cout<<x<<' '<<y<<endl;
f[j]=(x*h[0][0]+y*h[0][1]);
f[j+k/2]=(x*h[1][0]+y*h[1][1]);
// fr(I,0,len-1) cout<<f[I]<<' ';
// cout<<endl;
// f[j]=((x*h[0][0]%mod+mod)%mod+(y*h[0][1]%mod+mod)%mod+mod)%mod;
// f[j+k/2]=((x*h[1][0]%mod+mod)%mod+(y*h[1][1]%mod+mod)%mod+mod)%mod;
}
}
}
return;
}
int N;
inline void init(){
cin>>n;
N=(1<<n);
fr(i,0,N-1) cin>>a[i];
// fr(i,0,N-1) cout<<a[i]<<' ';
// puts("");
}
int siz[Maxn];
inline void copy(){
fr(i,0,N-1) aa[i]=a[i];
// fr(i,0,N-1) cout<<aa[i]<<' ';
// puts("");
}
inline void work1(const double c1[2][2],const double c2[2][2]){
copy();
// fr(i,0,N-1) cout<<aa[i]<<' ';
// puts("");
fwt(aa,N,c1);
// fr(i,0,N-1) cout<<aa[i]<<' ';
// puts("");
// fwt(aa,N,c2);
// fr(i,0,N-1) cout<<aa[i]<<' ';
// puts("");
}
inline void work(){
work1(cor,icor);
double ans=0;
fr(i,1,N-1){
siz[i]=siz[i/2]+(i&1);
double now;
now=1-aa[i^(N-1)];
if(now<(1e-8)) {
puts("INF");
return;
}
now=1.0/now;
if(siz[i]&1) ans=ans+now;
else ans=ans-now;
}
printf("%.8lf",ans);
}
signed main(){
// freopen("input.in","r",stdin);
// freopen("output.out","w",stdout);
init();
work();
// printf("\nTIME:%.3lf",(double)clock()/CLOCKS_PER_SEC);
return 0;
}